在我的场景中,我需要使用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)来获取行。
答案 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()
是其中之一
历史原因;没有合理使用的情况
它)。