scanf("%f%c", ...) 对输入 100e 做了什么?
What does scanf("%f%c", ...) do against input `100e`?
考虑以下 C
代码(在线可用 io.c):
#include <stdio.h>
int main () {
float f;
char c;
scanf ("%f%c", &f, &c);
printf ("%f \t %c", f, c);
return 0;
}
当输入100f
时,输出100.000000 f
。
但是,当输入100e
时,它只输出100.000000
,后面没有e
。这里发生了什么? 100e
不是无效的浮点数吗?
来自 C 标准(6.4.4.2 浮点常量)
decimal-floating-constant:
fractional-constant exponent-partopt floating-suffixopt
digit-sequence exponent-part floating-suffixopt
和
exponent-part:
e signopt digit-sequence
E signopt digit-sequence
如果您将 printf
的调用更改为以下方式
printf ("%e \t %d\n", f, c);
你会得到输出
1.000000e+02 10
即变量c
得到换行符'\n'
.
似乎 scanf 的实现方式是将符号 e
解释为浮点数的一部分,尽管该符号后没有数字。
根据C标准(7.21.6.2 fscanf函数)
9 An input item is read from the stream, unless the specification
includes an n specifier. An input item is defined as the longest
sequence of input characters which does not exceed any specified
field width and which is, or is a prefix of, a matching input
sequence.278) The first character, if any, after the input item
remains unread.
所以100e
是一个浮点数匹配的字符输入序列
像那样在 "%f %c"
之间输入一个 space 并且当你要输入时确保在两个输入之间有一个 space。
我假设您只想打印一个字符。
这是(可以说)glibc bug。
这种行为明显违反了标准。然而,它是由其他实现展示的。 Some people consider it a bug in the standard instead.
根据标准,输入项定义为最长的输入字符序列,不超过任何指定的字段宽度,并且是匹配输入序列的前缀。所以 100e
是一个输入项,因为它是匹配输入序列的前缀,例如 100e1
,但输入中任何更长的字符序列都不是。此外,如果输入项不是匹配序列,指令执行失败:这种情况是匹配失败。 100e
不是匹配序列,因此标准要求指令失败。
标准无法告诉 scanf
接受 100
并继续从 e
开始扫描,正如某些人所期望的那样,因为 stdio
有一个有限的推回只有一个字符。因此,在读取 100e
之后,实现必须至少再读取一个字符,具体地说是一个换行符,然后将换行符和 e
都推回,但它并不总是这样做。
我想说这显然是一个非常模糊的灰色地带。
如果您是 C 库的实现者(或 X3J11 委员会的成员),您必须担心这类事情 — 有时会担心很多。你必须担心边缘情况,有时边缘情况可能特别急躁。
但是,您没有用“语言律师”标签标记您的问题,所以您可能不担心严格正确的官方解释。
如果您不是 C 库的实现者或 X3J11 委员会的成员,我会说:不要 担心这里的“正确”答案是什么!你不必担心,因为你不在乎,因为你会疯狂地编写对这个问题敏感的代码——正是因为它是一个如此明显的灰色地带。 (即使您 确实 弄清楚这里的正确行为是什么,您是否相信世界上每个 C 库的每个实现者总是会实现该行为?)
我想说在“不担心”的范畴内你可以做三件事,并且不要编写对这个问题敏感的代码。
根本不要使用 scanf
(用于任何事情)。这是一个令人厌恶的、不精确的、不完美的函数,除了——也许——在你第一次学习 C 时将数字输入到你编写的前几个程序中之外,它没有任何用处。在那之后,scanf
在任何严肃的场合都没有用程序。
不要安排您的代码和数据,使其首先必须面对诸如“100e
”之类的模棱两可的输入。无论如何,它是从哪里来的?它是用户可能输入的输入吗?从数据文件中读取数据?是预期的还是意外的,正确的还是错误的输入?如果您正在读取数据文件,您是否可以控制写入数据文件的代码?你能保证浮点字段总是被适当地定界吗,不会偶尔会附加随机字母字符吗?
如果您 do 必须解析可能包含有效浮点数的输入,可能会附加随机字母字符,因此可能会产生歧义,例如对此,我鼓励您改用 strtod
,它可能定义得更好,实现也更好。
考虑以下 C
代码(在线可用 io.c):
#include <stdio.h>
int main () {
float f;
char c;
scanf ("%f%c", &f, &c);
printf ("%f \t %c", f, c);
return 0;
}
当输入100f
时,输出100.000000 f
。
但是,当输入100e
时,它只输出100.000000
,后面没有e
。这里发生了什么? 100e
不是无效的浮点数吗?
来自 C 标准(6.4.4.2 浮点常量)
decimal-floating-constant:
fractional-constant exponent-partopt floating-suffixopt
digit-sequence exponent-part floating-suffixopt
和
exponent-part:
e signopt digit-sequence
E signopt digit-sequence
如果您将 printf
的调用更改为以下方式
printf ("%e \t %d\n", f, c);
你会得到输出
1.000000e+02 10
即变量c
得到换行符'\n'
.
似乎 scanf 的实现方式是将符号 e
解释为浮点数的一部分,尽管该符号后没有数字。
根据C标准(7.21.6.2 fscanf函数)
9 An input item is read from the stream, unless the specification includes an n specifier. An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence.278) The first character, if any, after the input item remains unread.
所以100e
是一个浮点数匹配的字符输入序列
像那样在 "%f %c"
之间输入一个 space 并且当你要输入时确保在两个输入之间有一个 space。
我假设您只想打印一个字符。
这是(可以说)glibc bug。
这种行为明显违反了标准。然而,它是由其他实现展示的。 Some people consider it a bug in the standard instead.
根据标准,输入项定义为最长的输入字符序列,不超过任何指定的字段宽度,并且是匹配输入序列的前缀。所以 100e
是一个输入项,因为它是匹配输入序列的前缀,例如 100e1
,但输入中任何更长的字符序列都不是。此外,如果输入项不是匹配序列,指令执行失败:这种情况是匹配失败。 100e
不是匹配序列,因此标准要求指令失败。
标准无法告诉 scanf
接受 100
并继续从 e
开始扫描,正如某些人所期望的那样,因为 stdio
有一个有限的推回只有一个字符。因此,在读取 100e
之后,实现必须至少再读取一个字符,具体地说是一个换行符,然后将换行符和 e
都推回,但它并不总是这样做。
我想说这显然是一个非常模糊的灰色地带。
如果您是 C 库的实现者(或 X3J11 委员会的成员),您必须担心这类事情 — 有时会担心很多。你必须担心边缘情况,有时边缘情况可能特别急躁。
但是,您没有用“语言律师”标签标记您的问题,所以您可能不担心严格正确的官方解释。
如果您不是 C 库的实现者或 X3J11 委员会的成员,我会说:不要 担心这里的“正确”答案是什么!你不必担心,因为你不在乎,因为你会疯狂地编写对这个问题敏感的代码——正是因为它是一个如此明显的灰色地带。 (即使您 确实 弄清楚这里的正确行为是什么,您是否相信世界上每个 C 库的每个实现者总是会实现该行为?)
我想说在“不担心”的范畴内你可以做三件事,并且不要编写对这个问题敏感的代码。
根本不要使用
scanf
(用于任何事情)。这是一个令人厌恶的、不精确的、不完美的函数,除了——也许——在你第一次学习 C 时将数字输入到你编写的前几个程序中之外,它没有任何用处。在那之后,scanf
在任何严肃的场合都没有用程序。不要安排您的代码和数据,使其首先必须面对诸如“
100e
”之类的模棱两可的输入。无论如何,它是从哪里来的?它是用户可能输入的输入吗?从数据文件中读取数据?是预期的还是意外的,正确的还是错误的输入?如果您正在读取数据文件,您是否可以控制写入数据文件的代码?你能保证浮点字段总是被适当地定界吗,不会偶尔会附加随机字母字符吗?如果您 do 必须解析可能包含有效浮点数的输入,可能会附加随机字母字符,因此可能会产生歧义,例如对此,我鼓励您改用
strtod
,它可能定义得更好,实现也更好。