8

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

CSV 文件的一行示例:

1,Mr,James,Quigley,董事,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;
4

2 回答 2

17

这是因为一个字符串%s可以包含逗号,所以它会被扫描到第一个字符串中。scanf()格式说明符中没有“前瞻”,%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()换行符只是空格。

于 2013-09-11T09:22:21.180 回答
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 文件中的值读取到变量中

于 2013-09-11T09:25:25.293 回答