从CSV文件中读取值并存储到变量中

8

我正在尝试编写一段简单的代码,将CSV文件中的值读取到一个包含最多100个条目的结构体数组中。

CSV文件中的一行示例:

1,Mr,James,Quigley,Director,200000,0

我使用以下代码读取这些值,但当我打印出这些值时,它们是不正确的。

for(i = 0; i < 3; i++) /*just assuming number of entries here to demonstrate problem*/
    {
    fscanf(f, "%d,%s,%s,%s,%s,%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
    } 

当我打印第一个名字时,所有的值都被分配给了第一个名字:

for(j = 0; j < 3; j++) /* test by printing values*/
    {
    printf("Employee name is %s\n", inArray[j].firstName);
    } 

以这种方式提供ames,Quigley,Director,200000,0等内容。我确定这是我的格式化fscanf行的方式,但我无法使其工作。

这是我正在读取的结构:

typedef struct Employee
    {
    int ID;
    char salutation[4];
    char firstName[21];
    char surName[31];
    char position[16];
    int sal;
    int deleted;
    } Employee;

2
%s 是贪婪的,我认为它会读取整个单词... 它会先找到 %d,即整数部分,然后是逗号, ,之后必须读取字符串。,在字符串中是有效的,所以它会一直读取直到行末(此时没有空格),而不是第一个逗号...剩余的部分则为空。(引用自这篇答案) - ppeterka
1
你在帖子中提到了 firstN 和 firstName - 它是哪一个?你能同时发布结构体吗? - doctorlove
更正了变量名称并添加结构体。 - Dawson
2个回答

17
这是因为一个字符串%s可以包含逗号,所以它被扫描到第一个字符串中。在scanf()格式说明符中没有"look-ahead",%s之后跟随逗号在格式说明字符串中并不起作用。
使用字符组(查找手册中的[)。
const int got = fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID,
                       inArray[i].salutation, inArray[i].firstName,
                       inArray[i].surName, inArray[i].position, &inArray[i].sal, 
                       &inArray[i].deleted);

学会检查返回值,因为I/O调用可能会失败!除非got是7,不要依赖数据有效。

为了使你的程序读取整个文件(多个记录,即多行),我建议使用fgets()将整行加载到(大型)固定大小的缓冲区中,然后使用sscanf()在该缓冲区上解析列值。这样做更容易,并确保您确实扫描了单独的行,使用循环调用fscanf()则不会,因为对于fscanf(),换行符只是空格。


1
那么您最后的意思是,我应该将这个放在一个 while 循环中,直到 got = 7? - Dawson
1
@user2368481 实际上不是,我添加了更多的文本。 - unwind

2

我不妨把我的评论发布为答案:

%s 默认情况下读取一个完整的单词。

它找到了 %d,即整数部分,然后是 ,,接着它需要读取一个字符串。在单词中,, 被视为有效字符(不是空格),因此它会一直读取直到行末(此时没有空格),而不是第一个逗号……剩余部分则为空。(来源于这个回答)

您需要使用指定正则表达式来更改分隔符:

fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);

使用%[^,]代替%s,它的意思是“抓取所有字符,并在找到,时停止”。

编辑

%[^,]s不好,需要在扫描集结束后加上字面值s... 感谢 @MichaelPotter

(来自更改scanf()定界符从CSV文件中读取值到变量)


应该删除 s,因为 [^,] 充当了说明符。也就是说:"%[^,]s" 应该改为 "%[^,]"。由于编码方式的原因,scanf 将在每个字段的末尾寻找 s。如果找不到 s,则 scanf 将放弃解析。 - Be Kind To New Users
谢谢 @MichaelPotter,有时候我们的大脑会短路...这正是为什么结对编程可以极大地提高效率的原因... - ppeterka

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