正在分析字段中带有逗号的csv
Parsing a csv with comma in field
我正试图使用以下数据的csv创建一个对象
Alonso,Fernando,21,31,29,2,Racing
Dhoni,Mahendra Singh,22,30,4,26,Cricket
Wade,Dwyane,23,29.9,18.9,11,Basketball
Anthony,Carmelo,24,29.4,21.4,8,Basketball
Klitschko,Wladimir,25,28,24,4,Boxing
Manning,Peyton,26,27.1,15.1,12,Football
Stoudemire,Amar'e,27,26.7,21.7,5,Basketball
"Earnhardt, Jr.",Dale,28,25.9,14.9,11,Racing
Howard,Dwight,29,25.5,20.5,5,Basketball
Lee,Cliff,30,25.3,25.1,0.2,Baseball
Mauer,Joe,31,24.8,23,1.8,Baseball
Cabrera,Miguel,32,24.6,22.6,2,Baseball
Greinke,Zack,33,24.5,24.4,50,Baseball
Sharapova,Maria,34,24.4,2.4,22,Tennis
Jeter,Derek,35,24.3,15.3,9,Baseball
我使用以下代码来解析它:
void AthleteDatabase::createDatabase(void)
{
ifstream inFile(INPUT_FILE.c_str());
string inputString;
if(!inFile)
{
cout << "Error opening file for input: " << INPUT_FILE << endl;
}
else
{
getline(inFile, inputString);
while(inFile)
{
istringstream s(inputString);
string field;
string athleteArray[7];
int counter = 0;
while(getline(s, field, ','))
{
athleteArray[counter] = field;
counter++;
}
string lastName = athleteArray[0];
string firstName = athleteArray[1];
int rank = atoi(athleteArray[2].c_str());
float totalEarnings = strtof(athleteArray[3].c_str(), NULL);
float salary = strtof(athleteArray[4].c_str(), NULL);
float endorsements = strtof(athleteArray[5].c_str(), NULL);
string sport = athleteArray[6];
Athlete anAthlete(lastName, firstName, rank,
totalEarnings, salary, endorsements, sport);
athleteDatabaseBST.add(anAthlete);
display(anAthlete);
getline(inFile, inputString);
}
inFile.close();
}
}
我的代码在线中断:
"Earnhardt, Jr.",Dale,28,25.9,14.9,11,Racing
显然是因为那些名言。有更好的方法来处理这个问题吗?我对C++还很陌生,所以如果有任何帮助,我们将不胜感激。
我建议使用合适的CSV解析器。你可以在前面这个问题的答案中找到一些,或者在谷歌上搜索一个。
如果你坚持自己滚动,那么最简单的方法可能是从基础开始,将其设计为一个有限状态机,一次处理一个字符的输入。展望未来,您基本上需要两种状态:"读取正常输入"answers"读取带引号的字符串"。如果你不想使用前瞻性,你可以用更多的状态来实现,例如:
-
初始状态:如果下一个字符是引号,则切换到状态引号字段;否则就表现为,就好像处于状态未引用字段。
-
无引号字段:如果下一个字符是EOF,则结束解析;否则,如果是新行,则启动新行并切换到初始状态;否则,如果是分隔符(逗号),则在同一行中启动一个新字段,并切换到初始状态;否则将字符附加到当前字段,并保持状态未引用字段。(可选地,如果字符是引号,则发出解析错误信号。)
-
带引号字段:如果下一个字符是EOF,则表示解析错误;否则,如果是引号,则切换到状态结束引号;否则将字符附加到当前字段,并保持状态引用字段。
-
结束引号:如果下一个字符是引号,则将其附加到当前字段并返回到状态引号字段;否则,如果它是逗号、换行符或EOF,则表现为处于状态未引用字段;else信号解析错误。
(这是"传统"CSV,如RFC 4180中所述,其中引用字段中的引号通过加倍来转义。添加对反斜杠转义的支持(在CSV格式的一些相当常见的变体中使用)只是一个练习。它需要一个或两个以上的状态,这取决于您是希望支持带引号或不带引号的字符串中的反斜杠,还是同时支持传统转义和反斜杠转义。)
在高级脚本语言中,这种逐个字符的迭代确实效率很低,但由于您正在编写C++,它所需要的只是一些半吊子的I/O缓冲和一个相当高效的字符串附加操作。
您必须使用bool
标志和累积下一个字段内容的std::string
逐字符解析每一行;而不是像你那样费力地前进到下一个逗号。
最初,bool
标志为false,您可以逐个字符地对整行进行迭代。引号字符翻转bool
标志。逗号字符仅当bool标志为false时获取std::string
的累积内容并将其保存为行上的下一个字段,并将std::string
清除为空,为下一个域做好准备。否则,字符将被附加到缓冲区中。
这是算法的基本轮廓,还有一些小细节,你应该能够自己充实。还有其他几种方法可以做到这一点,效率略高,但对于像你这样的初学者来说,这种方法是最容易实现的。
简单答案:使用不同的分隔符。如果你使用'|'
这样的东西来代替,那么所有的东西都会更容易解析
Stoudemire,Amar'e|27|26.7|21.7|5|Basketball
Earnhardt, Jr.|Dale|28|25.9|14.9|11|Racing
其他任何可能需要解析文件的应用程序的优势也同样清晰。
如果需要使用逗号,那么您必须根据字段的第一个字符有条件地获取字段:
std::istream& nextField(std::istringstream& s, std::string& field)
{
char c;
if (s >> c) {
if (c == '"') {
// using " as the delimeter
getline(s, field, '"');
return s >> c; // for the subsequent comma
// could potentially assert for error-checking
}
else if (c == ',') {
// handle empty field case
field = "";
}
else {
// normal case, but prepend c
getline(s, field, ',');
field = c + field;
}
}
return s;
}
用作getline
:的替代品
while (nextField(s, field)) {
athleteVec.push_back(field); // prefer vector to array
}
如果我们有一个未终止的带引号的字符串:,甚至可以通过继续使用getline
来稍微简化这个逻辑
std::istream& nextField(std::istringstream& s, std::string& field)
{
if (std::getline(s, field, ',')) {
while (s && field[0] == '"' && field[field.size() - 1] != '"') {
std::string next;
std::getline(s, next, ',');
field += ',' + next;
}
if (field[0] == '"' && field[field.size() - 1] == '"') {
field = field.substr(1, field.size() - 2);
}
}
return s;
}
我同意Imari的回答,为什么要重新发明轮子?话虽如此,您是否考虑过regex?我相信这个答案可以用来完成你想要的,然后再做一些。
- 将结构字段的类型展开为可变模板参数
- 将位字段导出到数组
- 为了方便起见,我应该避免公开私有字段变量吗
- 当字段可以为null时,如何使用C++接口在Avro中写入数据
- 在java中读取c++字节的位字段
- 链接器找不到在虚拟类 c++ 中访问的静态字段的符号
- 私有字段对象与私有继承?
- 声明没有默认构造函数的字段
- C++内存模型和位字段的最大序列
- 声明为无效的变量或字段'...' Ardunio 编译器上的错误
- 如何在QByteArray中放置和检索位字段而不会感到痛苦?
- C++ win32 如何使密码字段可选并启用复制和粘贴?
- 如何通过UDP接收QByteArray并将其解析为位字段结构?
- 仅匹配集合中的某些字段
- 结构字段名称与 GDB 中的 STL 数组冲突
- 如何使用位字段将数据从二进制文件复制到结构中?
- 使用 istream 提取运算符进行 csv 解析:如何检测缺失的字段值?
- 从CSV文件中提取每个条目字段的最佳方法是什么
- Boost Tokenizer无法解析具有带有双引号的字段的CSV文件
- 正在分析字段中带有逗号的csv