在fscanf()函数中使用[]的含义

6

我有一个文本文件,内容如下:

"abc","def","ghi"

以下方法可正确读取文件内容:
int main()
{
    char name[1024] = {0};
    FILE *file = fopen("file.txt", "r");

    while(1)
    {
        if (fscanf(file, " %[\",]s ", name) == EOF)
            break;
        if (fscanf(file, " %[a-zA-Z]s ", name) == EOF)
            break;

        printf("%s\n", name);
    }

    return 0;
}

然而,以下内容失败了:
int main()
{
    char name[1024] = {0}, garbage[5];
    FILE *file = fopen("file.txt", "r");

    while(1)
    {
        if (fscanf(file, " %[\",]s%[a-zA-Z]s ", garbage, name) == EOF)
            break;

        printf("%s\n", name);
    }

    return 0;
}

我正在使用MSVC ++ 08,我缺少什么? 我正在寻找在while循环中使用单个fscanf()的解决方案。


2
虽然有很多建议可以解决您的问题,但最好的解决方案并不是使用 fscanf。如果您编写自己的词法分析状态机,将得到一个更加稳健的解决方案(即处理奇怪的边缘情况和畸形输入)。 - Oliver Charlesworth
@Oli:我同意。我正在解决一个编程问题,其中我不必处理奇怪的输入。对于所有有效输入案例,我更喜欢使用机器而不是手动完成。 - Donotalo
4个回答

7

这是运行了?纯属运气不好 :-)

你的转换规范意味着

" %[\",]s "
         ^= optionally skip whitespace
        ^== read a literal 's'
  ^^^^^^=== read an unlimited string of quotes and commas
 ^========= optionally skip whitespace

并且

" %[a-zA-Z]s "
            ^= optionally skip whitespace
           ^== read a literal 's'
  ^^^^^^^^^=== read an unlimited string of letters
 ^============ optionally skip whitespace

并且

" %[\",]s%[a-zA-Z]s "
                   ^= optionally skip whitespace
                  ^== read a literal 's'
         ^^^^^^^^^=== read an unlimited string of letters
        ^============ read a literal 's'
  ^^^^^^============= read an unlimited string of quotes and commas
 ^=================== optionally skip whitespace

我想你需要

" %4[\",]%1023[a-zA-Z] "
                      ^= optionally skip whitespace
         ^^^^^^^^^^^^^== read a string of at most 1023 letters
  ^^^^^^^=============== read a string of at most 4 quotes and commas
 ^====================== optionally skip whitespace

除此之外,scanf函数会返回成功转换的次数或错误时返回EOF。当您想要比较结果值时,应该将其与期望的转换次数进行比较,而不是与EOF进行比较(例如1或2)。

if (scanf() == 3) /* expected 3 conversions */
{ /* ok */ }
else { /* oops, something went wrong */ }

我以为's'是'string'的缩写。:( 我实际上完全忘记了[]的作用。在谷歌上找不到它。但纯属运气好;) 它最终奏效了! - Donotalo

4
  1. 括号是它们自己的转换说明符,而不是修改符。 %[a-zA-Z]s 表示“匹配任意数量的字母并分配给 name,然后匹配一个字面上的 s”。删除 s 字符。
  2. 如果你想匹配某些内容但将其丢弃,请使用星号而不是垃圾缓冲区:%*[\",]
  3. scanf 如果在文件结束之前至少匹配了一个说明符,则不会返回 EOF。这意味着当文件指针位于 i 之后时,您将得到一个错误的循环。考虑测试指定的说明符计数,或在末尾使用另一个 "%*[\"]" 来吸收尾随引号。

这也是为什么第一个版本起作用的原因。字面上的 s 未能匹配,但第一个转换成功了,所以你得到了 name,但没有得到 EOF


if (fscanf(file, " %*[\",]%[a-zA-Z] ", name) < 1)
    break;

或者

fscanf(file, " %*[\",]%[a-zA-Z]%*[\"] ", name)

2

删除"s"转换标志,如下:

if (fscanf(file, " %[\",]%[a-zA-Z] ", garbage, name) < 2)

请注意,我必须将其与EOF而不是2进行比较,因为最后的引号将在下一次迭代中被读入。
编辑:我很惊讶你的第一个代码示例也能正常工作,但它在Mac OS X上使用gcc运行得非常好,因此这不是Microsoft特定的问题。

0

以下应该可以工作:

      if (fscanf(file, " %[\",]%[a-zA-Z] ", garbage, name) == EOF) 
        break; 

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