繁体   English   中英

为什么scanf没有将字符恢复失败?

[英]Why doesn't scanf put characters back on failing?

我知道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 ,只要有可能,谢谢你的建议。

问题仍然存在:“ 为什么即使失败,scanf也会消耗输入?

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

在您的具体示例中,它取决于C标准库的实现以及它认为是float的有效表示。 例如,有成功解析字符串的实现,如nan (非数字)或inf (infinity)。 虽然我不能想到一个以no开头的有效float表示,但是你的库似乎知道一个,或者它是一个试图解析nan并且没有放回使其失败的o的bug。

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

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

根据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的标准符合实现,当作为%f指令的输入normal时, 必须仅消耗n (在输入上留下ormal )并且失败。 因此,如果你的implmentation也消耗了o ,那么你要么使用非“C”语言环境来接受以no开头的东西,要么stdlib实现中似乎存在错误。

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

平台: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

看起来它依赖于平台。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM