[英]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.