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

时间:2013-07-12 07:26:53

标签: 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,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设置的列表来保存数据。流程应该像找到第一个headerID1,然后找到下一行。如果下一行是dataline,则将其视为dataline属于headerID1。如果下一行是另一个headerID,则再次循环。

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

2 个答案:

答案 0 :(得分:1)

您的输入循环应该更像:

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...
     }
}

使用:

bool stats_with_and_remove(std::string& lhs, const std::string& rhs)
{
    if (lhs.compare(0, rhs.size(), lhs) == 0)  // rhs.size() > lhs.size() IS safe
    {
        lhs.erase(0, rhs.size());
        return true;
    }
    return false;
}

答案 1 :(得分:0)

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

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 = "";
    }
}

将它扩展为处理行尾注释相当容易 然而,这通常是一个很好的政策(在类似的情况下) 这个)修剪前导和尾随空格---尾随 特别是,因为人类读者在看时不会看到它 文件。

或者,当然,您可以使用正则表达式 你希望作为评论treet的行(“\ s *#。*”);这很有效 与您当前的定义一致,但并没有真正扩展 很好的结束评论,特别是如果你想允许 在您的字段中引用字符串中#

最后一条评论:你的循环不正确。你不测试 getline在使用其结果之前成功了 即使没有其他内容,file.good()也可能返回true 读。 (file.good()是其中之一 历史原因;没有合理使用的情况 它)。