使用fscanf()使用C解析逗号分隔文件

时间:2014-01-28 15:56:38

标签: c file-io scanf

我有一个包含这样数据的文件 -

Name, Age, Occupation
John, 14, Student
George, 14, Student
William, 23, Programmer

现在,我想读取数据,以便将每个值(例如姓名,年龄等)读取为字符串。
这是我的代码片段 -

....
if (!(ferror(input_fp) || ferror(output_fp))) {
    while(fscanf(input_fp, "%30[^ ,\n\t]%30[^ ,\n\t]%30[^ ,\n\t]", 
                name, age_array, occupation) != EOF){
        fprintf(stdout, "%-30s%-30s%-30s\n", name, age_array, occupation);
    }
    fclose(input_fp);
    fclose(output_fp);
}
....

然而,这会进入一个无限循环,给出一些随机输出 这就是我理解input conversion specifiers的方式 %30[^ ,\n\t] - >读取最多30个字符长的字符串,不要包括空格,逗号,换行符或制表符。
我正在阅读3这样的字符串 我哪里错了?

2 个答案:

答案 0 :(得分:7)

OP的

fscanf(input_fp, "%30[^ ,\n\t]%30[^ ,\n\t]%30[^ ,\n\t]", ...

不会消耗文本文件中的',''\n'。后续fscanf()次尝试也会失败并返回值0(不是EOF)会导致无限循环。


虽然OP请求fscanf()解决方案,但fgets()/sscanf()可以更好地处理潜在的IO和解析错误。

FILE *input_fp;
FILE *output_fp;
char buf[100];
while (fgets(buf, sizeof buf, input_fp) != NULL) {
  char name[30];  // Insure this size is 1 more than the width in scanf format.
  char age_array[30];
  char occupation[30];
  #define VFMT " %29[^ ,\n\t]"
  int n;  // Use to check for trailing junk

  if (3 == sscanf(buf, VFMT "," VFMT "," VFMT " %n", name, age_array,
      occupation, &n) && buf[n] == '\0') {
    // Suspect OP really wants this width to be 1 more
    if (fprintf(output_fp, "%-30s%-30s%-30s\n", name, age_array, occupation) < 0)
      break;
  } else
    break;  // format error
}
fclose(input_fp);
fclose(output_fp);

不是致电ferror(),而是检查fgets()fprintf()的返回值。

怀疑OP未声明的字段缓冲区为[30]并相应调整scanf()


[编辑]

有关if (3 == sscanf(buf, VFMT "," ...

的详细信息

if (3 == sscanf(...) && buf[n] == '\0') {在以下情况下成为真实:
1)确切地说3 "%29[^ ,\n\t]"格式说明每个scanf至少1 char 2)buf[n]是字符串的结尾。 n是通过"%n"说明符设置的。 ' '中的前一个" %n"会导致在最后"%29[^ ,\n\t]"之后使用任何后续空格。 scanf()看到"%n",指示它设置从扫描开始的当前偏移量,以便分配给int指向的&n

"VFMT "," VFMT "," VFMT " %n"由编译器连接到
" %29[^ ,\n\t], %29[^ ,\n\t], %29[^ ,\n\t] %n"
我发现前者比后者更容易维护。

" %29[^ ,\n\t]"中的第一个空格指示sscanf()扫描(消耗而非保存)0个或更多空格(' ''\t',{{1}等等)。其他人指示'\n'使用并保存任何 1到29 sscanf() ,但 char',',{{ 1}},然后追加'\n'

答案 1 :(得分:2)

您没有跳过值之间的实际逗号和空格。

第一个%30[^ ,\n\t]说明符匹配后,输入可能包含逗号和空格,格式字符串中的以下内容不匹配。

在输入中预期的格式字符串中添加逗号和空格:

while(fscanf(input_fp, "%30[^ ,\n\t], %30[^ ,\n\t], %30[^ ,\n\t]", name, age_array, occupation) == 3)
                                    ^             ^
                                    |             |
                                    \             /
                                   add these to make
                                   fscanf() skip them
                                      in the input!

此外,检查fscanf()的返回值是次优的:在依赖要转换的值之前,您应该检查返回值是否等于转换次数。

另外,你使用反斜杠续行字符是完全没有意义的,应该删除。