为什么scanf会在失败时重新放回字符?

时间:2017-05-23 15:34:31

标签: c scanf

我知道scanf(和family)返回它成功读取的参数数量。我也知道如果它失败了,输入仍然保持不变,所以你可以做这样的事情:

printf("%s", "Plese input a string or a float.\n");
float f;
char s[128];
if(scanf("%f", &f) == 1) {
    //do something to respond to user answering with a float. (1)
} else if(scanf("%127s", s)) {
    //do something to process the string. (2)
}

事实证明scanf 确实混淆了输入。我希望scanf尝试读取匹配<float here>的任何内容,并且在发生故障时不做任何事情,但是恰恰相反,scanf正在吃输入,直到它认为是一个好点为止停止。

例如:如果我输入1.2,我会按预期在分支(1)f = 1.2中结束。

如果我输入text结果符合预期,我最终会进入(2)s = "text"

但是,如果输入为normal,则结果是我在(2)中结束,没有任何额外的用户输入和值s = "rmal"。为什么消耗no超出了我的范围。

我会先发制人地指出,是的,我正在使用fgets代替scanf,只要有可能,感谢您的建议。

问题仍然存在:&#34; 为什么即使在发生故障时scanf也会消耗输入?&#34;

3 个答案:

答案 0 :(得分:3)

一般的答案是scanf()将一次读取一个字符,只要读取的字符可能导致转换成功,这将继续。只返回使转换失败的单个字符。 stdio FILE *流无法将多个字符放回去。

在具体示例中,它取决于C标准库的实现以及它认为float的有效表示。例如,有成功解析nan(非数字)或inf(无穷大)等字符串的实现。虽然我无法想到以float开头的有效no表示,但您的图书馆似乎知道一个,或者是尝试解析nan而不是放回o的错误这让它失败了。

话虽如此,我现在无法使用msvcrt在Windows上重现这一点。

长话短说,最好远离scanf()

答案 1 :(得分:3)

根据C99规范(7.19.6.2第9和12段):

  

这是   输入项被定义为输入字符的最长序列,不超过   任何指定的字段宽度,它是匹配的输入序列,或者是匹配输入序列的前缀。   输入项目之后的第一个字符(如果有)仍然未读。

     

a,e,f,g 匹配可选的带符号浮点数,无穷大或NaN,其   格式与strtod的主题序列的预期相同   功能

7.20.1.3第3段描述了strtod功能:

  

主题序列的预期形式是可选的加号或减号,然后是其中之一   以下内容:

     
      
  • 非空的十进制数字序列,可选地包含小数点   字符,然后是6.4.4.2中定义的可选指数部分;
  •   
  • 一个0x或0X,然后是一个非空的十六进制数字序列,可选地包含一个   小数点字符,然后是6.4.4.2;
  • 中定义的可选二进制指数部分   
  • INF或INFINITY之一,忽略大小写
  •   
  • NAN或NAN(n-char-sequence opt)之一,忽略NAN部分的情况,
  •   

第6段补充

  

除了“C”语言环境之外,可能还有其他特定于语言环境的主题序列形式   接受。

这意味着fscanf的标准符合实现,当normal作为%f指令的输入时,必须只消耗n(离开输入上ormal并失败。因此,如果您的implmentation也使用了o,那么您要么使用非“C”语言环境来接受以no开头的内容,要么stdlib实现中似乎存在错误。

答案 2 :(得分:2)

我做了快速检查,但我没有看到任何问题:

平台:Mac

编译器:GCC

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

$ gcc -Wall main.c

$ ./a.out
Plese input a string or a float.
1.2
B1 1.200000

$ ./a.out
Plese input a string or a float.
100
B1 100.000000

$ ./a.out
Plese input a string or a float.
test
B2 test

$ ./a.out
Plese input a string or a float.
normal
B2 normal        <<<<< No problem here

看起来它与平台有关。