C++ 如何使用C+跳过csv中的标题行+;

C++ 如何使用C+跳过csv中的标题行+;,c++,csv,C++,Csv,在我的场景中,我需要使用CSV创建一个参数文件。每一行表示一个配置数据,该行的第一个字段被视为标题,用作标识符。下面的CSV格式对我来说很容易解析: 1,field1,field2,field3,field4 // 1 indicated the TARGET that the other fields will be writted to. 1,field1,field2,field3,field4 2,field1,field2,field3,field4 2,field1,field2,f

在我的场景中,我需要使用CSV创建一个参数文件。每一行表示一个配置数据,该行的第一个字段被视为标题,用作标识符。下面的CSV格式对我来说很容易解析:

1,field1,field2,field3,field4 // 1 indicated the TARGET that the other fields will be writted to.
1,field1,field2,field3,field4
2,field1,field2,field3,field4
2,field1,field2,field3,field4........
但它对用户不友好。因此,我定义了一个csv文件,如下所示:

HeaderLine_Begin,1
field1,field2,field3,field4
field1,field2,field3,field4
HeaderLine_Begin,2
field1,field2,field3,field4
field1,field2,field3,field4
意思是,每一行都是由HeaderLine_Begin写入目标的数据。我只是将ID与实际数据分开。 然后,我创建如下结构:

    enum myenum
    {
      ON,OFF,NOCHANGE
    };

    struct Setting
    {
      int TargetID;

      string field1;
      string field2;
      myenum field3;
      myenum field4;    
    };
我知道如何编写一些代码来逐行读取csv,如下所示

filename +=".csv";

std::ifstream file(filename.c_str());
std::string line;

while ( file.good() )
{       
    getline ( file, line, '\n' ); // read a line until last 
    if(line.compare(0,1,"#") == 0) // ignore the comment line
        continue;

    ParseLine();// DONE.Parse the line if it's header row OR data row           
}

file.close(); // close file
我想做的是创建一个类似vetor设置的列表来保存数据。流程应该是,找到第一个headerID,然后找到下一行。如果下一行是dataline,则将其视为属于headerID的数据行。如果下一行是另一个headerID,则再次循环


问题是,在我找到headerRow之后,没有这样的std::getnextline(intLineIndex)供我获取行

您的输入循环应该更像:

int id = -1;
while (getline(file, line))
{
     if (line.empty() || line[0] == '#')
         continue;
     if (starts_with_and_remove(line, "HeaderLine_Begin,"))
         id = boost::lexical_cast<int>(line); // or id = atoi(line.c_str())
     else
     {
         assert(id != -1);
         ...parse CSV, knowing "id" is in effect...
     }
}

最简单的解决方案是使用正则表达式:

std::string line;
int currentId = 0;
while ( std::getline( source, line ) ) {
    trimCommentsAndWhiteSpace( line );
    static std::regex const header( "HeaderLine_Begin,(\\d+)" );
    std::smatch match;
    if ( line.empty() ) {
        //  ignore
    } else if ( std::regex_match( line, match, header ) ) {
        std::istringstream s( match[ 1 ] );
        s >> currentId;
    } else {
        //  ...
    }
}
我经常使用这种策略来解析
.ini
文件,这些文件 同样的问题:节标题的语法不同 其他事情

trimCommentsAndWhiteSpace
可以简单到:

void
trimCommentsAndWhiteSpace( std::string& line )
{
    if ( !line.empty() && line[0] == '#' ) {
        line = "";
    }
}
扩展它来处理行末注释是相当容易的 然而,这通常是一个好政策(在类似的情况下) 这)用于修剪前导空格和尾随空格---尾随空格 尤其是,因为人类读者在阅读时看不到它 档案

当然,您也可以使用正则表达式 要作为注释在树上显示的行(“\s*#。*”);这很有效 好吧,按照你目前的定义,但并没有真正扩展 对于行尾评论,尤其是如果您想允许
#
在字段中的带引号的字符串中

最后一点意见是:您的循环不正确。你不需要测试 在使用其结果之前,
getline
成功,并且
file.good()
阅读(
file.good()
就是其中之一 历史原因;在任何情况下使用它都是没有意义的

您可以先检查行是标题行还是数据行。但你可能知道这一点。您的问题到底是什么?如何仅针对数据行获取行()。感谢您的回复,但我不允许在我的解决方案中使用boost库。
boost::lexical_cast
实际上只是语法上的糖分,而且很容易自己实现,至少对于从字符串到字符串的转换是如此。C++11有
std::stoi
等,也可以使用。@James kanze谢谢,我understand@fanshaoer:根据注释,您可以用
id=atoi(line.c_str())替换
lexical_cast
如果您不想使用boost,但与
lexical_cast
不同的是,如果行没有有效的数字,它不会抛出异常,相反,
id
将设置为0。@TonyD如果您想使用C函数,
strtol
将是正确的,因为它支持错误处理。
void
trimCommentsAndWhiteSpace( std::string& line )
{
    if ( !line.empty() && line[0] == '#' ) {
        line = "";
    }
}