我知道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;
答案 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
看起来它与平台有关。