为什么';t scanf将字符放回失败位置?

为什么';t scanf将字符放回失败位置?,c,scanf,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("%1

我知道
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尝试读取与
匹配的任何内容,并且在出现故障时不执行任何操作,但是发生的情况是
scanf
吃掉输入,直到它认为合适的停止点为止

例如:如果我输入
1.2
,我将按预期进入分支
(1)
f=1.2

如果我输入
文本
结果与预期一致,我将进入
(2)
s=“text”

但是,如果输入是
正常的
,则结果是我在
(2)
中结束,没有任何额外的用户输入和
s=“rmal”
的值。我无法理解为什么
没有
被消费

我要先发制人地指出,是的,我使用的是
fgets
而不是
scanf
,如果可能的话,谢谢你的建议


问题仍然是:“为什么scanf即使失败也会使用输入?”

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

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

也就是说,我现在无法在windows上使用
msvcrt
复制此内容


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

我做了一个快速检查,没有发现任何问题:

平台:Mac

编译器:GCC

$gcc--版本
配置为:--prefix=/Applications/Xcode.app/Contents/Developer/usr--gxx include dir=/usr/include/c++/4.2.1
苹果LLVM 8.0.0版(clang-800.0.42.1)
目标:x86_64-apple-darwin15.6.0
线程模型:posix
InstalledDir:/Applications/Xcode.app/Contents/Developer/Toolchains/xcodefault.xctoolchain/usr/bin
$gcc-墙干管.c
美元/年
请输入字符串或浮点数。
1.2
B1 1.200000
美元/年
请输入字符串或浮点数。
100
B1 100.000000
美元/年
请输入字符串或浮点数。
测试
B2试验
美元/年
请输入字符串或浮点数。
正常的
B2正常根据C99规范(7.19.6.2第9和12段):

一个 输入项定义为输入字符的最长序列,不超过 任何指定的字段宽度,它是匹配输入序列的前缀或前缀。 输入项后的第一个字符(如果有)保持未读状态

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

7.20.1.3第3段描述了STROD功能:

主题序列的预期形式是可选的加号或减号,然后是 以下是:

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

在“C”语言环境以外的其他语言环境中,可以使用其他特定于语言环境的主题序列形式 接受


这意味着fscanf的标准一致性实现,当作为
%f
指令的输入给出
normal
时,必须只消耗
n
(将
normal
保留在输入上)并失败。因此,如果您的实现也在使用
o
,那么要么您使用的是非“C”语言环境,它接受以
no
开头的内容,要么stdlib实现中似乎有一个bug。

“我也知道,如果它失败,输入将保持不变”-不,它不会。不过,未读输入将保持不变。也许您正在阅读有关
sscanf
?请注意,如果您有多个转换规范,而第二个规范(例如)失败,则不会尝试第三个和后续转换。除空格和转换规范之外的字符也必须匹配,除非它们出现在最后一个转换规范之后,否则将报告失败。此外,
scanf()
使用一个pushback字符,这是通过
ungetc()
@trentcl获得的所有标准保证。呃,我想我不应该这样说。我以为是这样的。问题仍然是一样的。@JonathanLeffler为什么当他键入
文本
而不是
正常
时,它会像他预期的那样工作?它们都应该在第一个字符上失败,并将其推回。很好,这不仅是不一致的,而且取决于实现。太棒了。我不确定解析
nan
inf
是否是
scanf(“%f”,&f)
的要求。。。如果是,
msvcrt
是不合格的。顺便问一下,你知道<