根据1999年的C标准,scanf()
和strtod()
应接受无穷大和NaN作为输入(如果实现支持)。
两种功能的描述都有特殊的语言,可能会有解释。
scanf()
:
输入项被定义为输入字符的最长序列 它不超过任何指定的字段宽度,是或是 匹配输入序列的前缀。
strtod()
:
主题序列被定义为最长的初始子序列 输入字符串,从第一个非空白字符开始, 这是预期的形式。
虽然后者摘录似乎严格要求特定形式的" INF"," INFINITY"," NAN"或者" NAN(n-char-sequence-opt)",前者不是,并且人们会认为以下代码应该产生无穷大和NaN,因为字段宽度涵盖了匹配输入序列的前缀:
int r;
double d;
d = 0; r = sscanf("inf", "%2le", &d);
printf("%d %e\n", r, d);
d = 0; r = sscanf("nan", "%2le", &d);
printf("%d %e\n", r, d);
scanf()
上还有这个位:
a,e,f,g匹配一个可选的有符号浮点数,无穷大, 或NaN,其格式与主题序列的预期相同 strtod功能。相应的参数应为指针 浮动。
这是否仅仅是未能记录字段宽度为2,这比预期的最短形式(" inf"或#34; nan")更小匹配前缀" in"和" na"有效的匹配?
答案 0 :(得分:4)
在scanf
的行为规范中,“输入项”恰好是格式说明符处理所消耗的输入字符序列。在处理格式说明符之后,无论该处理是否成功,流都精确地定位在输入项中的最后一个字符之后,如紧接在问题中引用的“输入项”的定义之后的句子所清楚的那样。
输入项目保持未读后的第一个字符(如果有)。
读取输入项后,scanf
进入下一步(同一条款的第10段),其中输入项作为一个整体必须根据格式说明符:
10 ... [输入项]转换为适合转换说明符的类型。如果输入项不是匹配序列,则指令的执行失败:此条件是匹配失败。
“匹配序列”在每个格式说明符的描述中定义;对于f
说明符将是:
与
strtod
函数的主题序列的预期相同。
在问题中引用。
这与strtod
使用的算法不同。 strtod
找到最长的匹配序列,如果有一个(即使只有一个字符),它会转换它并将下一个字符的地址放在提供的endptr
参数中。
相比之下,scanf
必须处理输入流的限制,这不允许多个字符可靠地重写读指针。 (参见ungetc
的定义。)所以scanf
读取,直到找到一个无法扩展匹配的字符,此时它将该字符替换为输入流并尝试转换已读取的内容到那时。与strtod
不同,它不能回溯到较短的有效序列(如果有的话)。
与strtod
的另一个区别是scanf
可以限制为最大长度,对于转换无限长的固定长度输入字段非常有用。对于strtod
,有必要制作固定长度字段的NUL终止副本,或者在适当的点临时插入NUL,然后恢复被覆盖的字符。在这种情况下,验证strtod
消耗了整个输入是很重要的;如果没有,则表示输入中存在垃圾。
可以看到差异 可凭经验看出。给定输入1E-@
,scanf
将报告匹配失败,后续getchar
将返回'@'
。 strtod
将1.0
返回,endptr
指向E
。
指定的格式长度也会导致scanf
返回匹配的错误。给定输入1E-7@
,scanf
格式%2f
和%3f
将; %1f
将转换1.0
,%4f
(或更大)将转换.01
,将@
留给下一个说明符(或后续输入函数)。应用于%2f
或inf
的输入的nan
应表现出与1E-7
完全相同的行为:吸收两个字符后匹配失败(因为截断的字段不是有效的)浮点数。)。
上述是否发生取决于标准C库的实现是否符合标准。 Glibc没有,glibc很可能是你将在Linux平台上使用的,无论你是用gcc还是clang编译,因为clang没有捆绑标准的C库,甚至在libcxx项目中都没有。
我在Windows上进行的有限测试(使用在线编译器)表明scanf的libcrt实现确实可以正常工作。我自己对FreeBSD库的源代码的检查表明,scanf
会正确地报告匹配的失败,但可能会将读取的光标向上移回多个字符。
答案 1 :(得分:2)
没有必要记录这个具体案例,因为它已经在标准的一个引号中涵盖了:
输入项被定义为输入字符的最长序列 它不超过任何指定的字段宽度,是或是 匹配输入序列的前缀。
考虑这个简单的案例:
int ch;
int res;
const char *input_fmt;
ch = 0;
input_fmt = "%*d%c";
res = sscanf("123456789012345678901234567890abc", input_fmt, &ch);
printf("%d\t%c\n", res, ch);
// 1 a
由于%d
转换规范没有字段宽度,因此它将匹配无限多个字符0-9,这就是扫描完成后ch=='a'
的原因; n
的值在这里并不重要。与使用input_fmt="%*2d%c"
的输出对比:
1 3
第一个转化规范的字段宽度限制为2个字符,因此%c
匹配3
。这是"前缀" bit进来。12
是123456...
的前缀,与%d
转换规范相匹配。另一个例子:
input_fmt = "%*2d%c";
res = sscanf("1a3456789012345678901234567890bc", input_fmt, &ch);
printf("%d\t%c\n", res, ch);
// 1 a
字段宽度将匹配%d
的字符数限制为2,但匹配的输入序列长度为1,因为a
不是十进制整数。
字段宽度限制匹配输入是您在inf
和nan
进行的操作:
double d;
int r;
char s[10];
// 0 0.000000 ""
*s = 0; d = 0; r = sscanf("infinity", "%2le%9s", &d, s);
printf("%d\t%f\t\"%s\"\n", r, d, s);
// 2 inf "inity"
*s = 0; d = 0; r = sscanf("infinity", "%3le%9s", &d, s);
printf("%d\t%f\t\"%s\"\n", r, d, s);
// 0 0.000000 ""
*s = 0; d = 0; r = sscanf("-infinity", "%3le%9s", &d, s);
printf("%d\t%f\t\"%s\"\n", r, d, s);
// 2 -inf "inity"
*s = 0; d = 0; r = sscanf("-infinity", "%4le%9s", &d, s);
printf("%d\t%f\t\"%s\"\n", r, d, s);
字段宽度限制输入序列中的字符数,这可能会阻止识别-inf
和nan
等特殊格式。该行为已被记录,但也无法立即完全理解。