我知道scanf(和系列)返回它成功读取的参数数量。我还知道,如果失败,输入将保持不变,因此您可以执行以下操作:
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)
}
Run Code Online (Sandbox Code Playgroud)
事实证明这scanf 确实扰乱了输入。我希望 scanf 尝试读取任何匹配的内容<float here>,并且在失败的情况下不执行任何操作,但相反发生的情况是scanf吃掉输入,直到它认为适合停止的任何点。
例如:如果我输入1.2,我最终会按预期(1)进入分支。f = 1.2
如果我输入的text结果符合预期,我最终会得到(2)和s = "text"。
但是,如果输入是normal结果,我最终会得到(2)没有任何额外的用户输入和s = "rmal". 为什么被no消耗超出我的范围。
我要先指出,是的,我正在使用fgets代替scanf,只要有可能,谢谢您的建议。
问题仍然是一样的:“为什么 scanf 即使失败也会消耗输入? ”
小智 5
一般的答案是scanf()一次读取一个字符,只要读取的字符可能导致成功转换,这种情况就会持续下去。仅放回导致转换失败的单个字符。无论如何,流中没有办法stdio FILE *放回多个字符。
在您的具体示例中,它取决于 C 标准库的实现以及它认为float. 例如,有一些实现可以成功解析nan(不是数字)或inf(无穷大)等字符串。虽然我无法想到float以 开头的有效表示no,但您的库似乎知道一个,或者这是一个尝试解析nan而不放回o导致失败的错误。
话虽这么说,我现在无法在 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”区域之外,还可以接受其他区域特定的主题序列形式。
normal这意味着,当作为指令的输入给出时%f,符合标准的 fscanf 实现必须仅消耗n(保留ormal输入)并失败。因此,如果您的实现也消耗o,那么您使用的非“C”语言环境接受以 开头的内容no,或者 stdlib 实现中似乎存在错误。