使用C++从.csv文件中读取数据

26

我有一段代码,它应该在控制台中输出来自.csv文件的信息;

while(file.good())
{

    getline(file, ID, ',');
    cout << "ID: " << ID << " " ; 

    getline(file, nome, ',') ;
    cout << "User: " << nome << " " ;

    getline(file, idade, ',') ;
    cout << "Idade: " << idade << " "  ; 

    getline(file, genero, ' ') ; 
    cout << "Sexo: " <<  genero<< " "  ;

}

还有一个csv文件,打开后内容如下(用记事本打开):

0,Filipe,19,M

1,Maria,20,F

2,Walter,60,M

每当我运行程序时,控制台都会显示以下内容:

Unexpected output

我的问题是为什么程序不会在每一行重复那些cout信息,而只在第一行输出?

顺便提一下,nome表示名字,idade表示年龄,genero/sexo表示性别,在创建这篇文章之前忘了翻译。


1
我正在寻找一种简单的方法来做这件事,如果可能的话。你发的链接对于我的当前知识来说就像中文一样。 - Mr. Phil
5
那是C#,这是C++。 - Eddy Luten
@FilipeGama:你有两个问题,(A)答案中提到的分隔符,和(B)你在错误的位置检查输入是否有效。如果你只修复A,这将导致最后一行出现重复。 - Mooing Duck
@Mooing Duck 是的,有时会出现这种情况,但我不知道原因,你能告诉我应该在哪里检查输入是否有效吗? - Mr. Phil
@FilipeGama:如果进行输入操作,请测试其是否成功。流会传播失败,因此您只需要测试最后一个是否成功,这非常棒。 - Mooing Duck
4个回答

28

你可以按照这个答案的方法,了解在C++中处理CSV文件的多种不同方式。

在你的情况下,最后一次调用getline实际上将第一行的最后一个字段和所有剩余的行都放入变量genero中。这是因为在文件结束之前找不到空格分隔符。尝试将空格字符改为换行符:

    getline(file, genero, file.widen('\n'));

更简洁地说:

    getline(file, genero);

此外,你对于file.good()的检查是过早的。文件中的最后一个换行符在下一次为ID调用getline()时被丢弃之前仍然留在输入流中。直到这一点才检测到文件末尾,因此应该基于这个来进行检查。你可以通过将while测试改为基于ID本身的getline()调用(假设每行格式正确)来解决这个问题。

while (getline(file, ID, ',')) {
    cout << "ID: " << ID << " " ; 

    getline(file, nome, ',') ;
    cout << "User: " << nome << " " ;

    getline(file, idade, ',') ;
    cout << "Idade: " << idade << " "  ; 

    getline(file, genero);
    cout << "Sexo: " <<  genero<< " "  ;
}

为了进行更好的错误检查,您应该检查每个对getline()的调用结果。


@FilipeGama:你试过我的建议修复了吗? - jxh
是的,第二个方法可行 :) 谢谢,正如我在其他答案中所说,我想点赞但因为没有足够的积分而无法点赞。 - Mr. Phil
@user315052:有两个问题:(A)答案中提到的分隔符,以及(B)您检查输入是否有效的位置不正确。如果您只修复A,这将导致最后一行出现重复。 - Mooing Duck
@user315052 谢谢,我刚刚接受了你的答案,因为它是最完整的,也感谢MooingDuck。恭喜,目前还没有点赞的方式。 - Mr. Phil

13

CSV文件与任何其他文件一样,都是由字符流组成。getline函数从文件中读取,直到遇到分隔符,但在您的情况下,最后一个项目的分隔符并不像您认为的那样是空格。

getline(file, genero, ' ') ; 

这是换行符 \n

所以将那一行改为

getline(file, genero); // \n is default delimiter

1
正是我想要写的,所以+1。这段代码没有寻找换行符,导致其余部分无法正确解析。 - Refugnic Eternium
谢谢,问题终于解决了 :) 我想给你点赞,但是我没有足够的积分,所以在这里留下我的评论,恭喜! - Mr. Phil
@FilipeGama “点赞”不是唯一的表达“谢谢”的方式。接受他们的答案也是另一种完全有效的欣赏他们努力的方式。 - Refugnic Eternium
@claptrap:有两个问题:(A)答案中提到的分隔符,以及(B)您在错误的位置检查输入是否有效。如果只修复 A,则会导致最后一行出现重复。 - Mooing Duck
@Mooing Duck 是对的,我后来才意识到...它正在复制最后一行,那我该怎么做呢?提前感谢您的帮助 :) - Mr. Phil
显示剩余2条评论

4
你的csv文件格式不正确,输出结果不是三个循环而只是一个输出。 要确保只有一个循环,请添加一个计数器并在每次循环时将其递增。它只应计数到1。
这是你的代码看到的。
0,Filipe,19,M\n1,Maria,20,F\n2,Walter,60,M

试试这个

0,Filipe,19,M
1,Maria,20,F
2,Walter,60,M


while(file.good())
{

    getline(file, ID, ',');
    cout << "ID: " << ID << " " ; 

    getline(file, nome, ',') ;
    cout << "User: " << nome << " " ;

    getline(file, idade, ',') ;
    cout << "Idade: " << idade << " "  ; 

    getline(file, genero) ; \\ diff
    cout << "Sexo: " <<  genero;\\diff


}

如果不是3个循环,那么它如何显示第二行和第三行(即使没有couts)?抱歉,我很难理解这些.csv概念,提前感谢。编辑:如果您可以编辑我的代码并在此处发布,我将非常感激 :) - Mr. Phil
它有点起作用了,但现在我得到了这个: https://dl.dropboxusercontent.com/u/4613740/console2.png由于某种原因,它重复了最后一行... - Mr. Phil
这种方法只对第一行有效,其他行会出现问题...可以尝试这种方式:https://dl.dropboxusercontent.com/u/4613740/console3.png - Mr. Phil
之前没有注意到逗号,还是这样: https://dl.dropboxusercontent.com/u/4613740/console4.png重复了最后一行并在ID之前插入了新行 :( 这让我疯了... - Mr. Phil
我已经纠正了代码和csv文件(之前我没有编译器)。重复的行是由于您的.csv文件末尾有一个空行造成的。 - Ian Jenkins
@IanJenkins:正确的代码会减少这个问题的影响:http://coliru.stacked-crooked.com/view?id=13ce5945dc6733d4745c9e1e50813ce5-50d9cfc8a1d350e7409e81e87c2653ba - Mooing Duck

-6

这是因为你的 CSV 文件格式不正确, 可能是文本文件中的换行符不是 \n 或 \r

而且, 使用 C/C++ 解析文本并不是一个好主意。 尝试使用 awk:

 $awk -F"," '{print "ID="$1"\tName="$2"\tAge="$3"\tGender="$4}' 1.csv
 ID=0   Name=Filipe Age=19  Gender=M
 ID=1   Name=Maria  Age=20  Gender=F
 ID=2   Name=Walter Age=60  Gender=M

谢谢你的回答,虽然我不知道 awk 是如何工作的,你知道有哪些有用的链接吗? - Mr. Phil
看起来你正在使用Windows。这里有一个Windows版本的GAWK - Freeman Zhang
非常感谢你的帮助,但我已经在工作了 :) - Mr. Phil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接