C++ 如何使用C+跳过csv中的标题行+;
在我的场景中,我需要使用CSV创建一个参数文件。每一行表示一个配置数据,该行的第一个字段被视为标题,用作标识符。下面的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
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 = "";
}
}