为什么scanf解析“2E”而不解析“.” (使用 GCC)作为浮点数的“匹配输入序列的前缀”?

问题描述 投票:0回答:1

根据标准(C17草案,6.4.4.2¶1),

2E0
(2.0)和
.5
(0.5)是有效的浮动常数,而
2E
.
不是。

然而,使用 GCC,

scanf
2E
解析为 2.0,但无法将
.
解析为任何内容
:

#include <stdio.h>

int main(void) {
    float fl;
    char c;

    printf("Please enter a floating-point number: ");
    if (scanf("%f", &fl) == 1)
        printf("<%.2f>\n", fl);
    if (scanf("%c", &c) == 1)
        printf("[%c]\n", c);

    return 0;
}

使用 GCC(12.2.0、Windows 上的 MinGW),上述代码会生成 (

gcc -std=c17 -pedantic -Wall -Wextra
):

Please enter a floating-point number: 2Eq
<2.00>
[q]
Please enter a floating-point number: .q
[q]

使用 MSVC (19.35.32217.1),我得到 (

cl /std:c17 /Wall
):

Please enter a floating-point number: 2Eq
[q]
Please enter a floating-point number: .q
[q]

这里,

q
用作虚拟字符,以演示先前对
scanf
的调用消耗了多少输入缓冲区。

(让我们忽略这样一个事实,即不清楚浮点数

.
“应该”代表什么:0 或 1。)


让我们试着理解这一点。与此相关的似乎是标准中的以下条款(C17 草案,7.21.6.2 ¶9):

从流中读取输入项,除非规范包含 n 说明符。输入项被定义为最长的输入字符序列,该序列不超过任何指定的字段宽度,并且是匹配输入序列或其前缀。291) 输入项之后的第一个字符(如果有)仍未阅读。如果输入项的长度为零,则指令执行失败;这种情况是匹配失败,除非文件结尾、编码错误或读取错误阻止了流的输入,在这种情况下,它是输入失败。

291)

fscanf
最多将一个输入字符推回到输入流上。因此,一些
strtod
strtol
等可以接受的序列,对于
fscanf
来说是不可接受的。

据我所知,该标准关于

strtod
和朋友(此处:
strtof
)与我的问题最相关的条款是 7.22.1.3 ¶3(此处未复制),从中可以得出
2E0 
(2.0) 和
.5
(0.5) 是有效的“主题序列”(在 7.22.1.3 ¶2 的意义上),而
2E
.
则不是。

如果我正确理解第 7.21.6.2 节 ¶9 条,则只有 0 长度的输入项才相当于“匹配失败”或“输入失败”。因为 2E. 都是匹配输入序列

的有效 
前缀(尽管不是完整的
匹配输入序列
),所以两者都不是匹配失败或输入失败。 因此我们可以问:为什么scanf

2E解析为浮点数,而不是

.

这可能与“匹配输入序列的前缀”定义的微妙之处有关。

第 7.22.1.3 ¶3 条(关于
    strtod
  • /
  • strtof
  • /
    strtold
    )周围的细节也可能是相关的,因为它们与
    1
    和朋友的
    scanf
    的推回限制有关。
    
    
    我相信以下代码说明了“匹配输入序列的前缀”的概念:
#include <stdio.h> int main(void) { char p[5] = "pppp", q[5] = "qqqq"; int i; i = sscanf("ABCD", "%2c%4c", p, q); printf("<%s>\n", p); /* <ABpp> */ printf("<%s>\n", q); /* <CDqq> */ printf("%d\n", i); /* 2 */ return 0; }

此处,尽管

%4c
 / 
q

的匹配需要正好 4 个字符(C17 草案,7.21.6.2 ¶12,项目

c
;此处未复制有关多字节字符的不相关脚注)

匹配字段宽度所指定数量的字符序列(如果指令中不存在字段宽度,则为 1)。[
fn

]

CD

是有效的“匹配输入序列的前缀”,因此该代码不会导致匹配失败或输入失败。 (鉴于赋值可能比给定的字段宽度短,我发现标准使用“完全”一词令人困惑。)

我发现了2个类似的问题(此处列出,排名不分先后):


“scanf”与不完整的指数部分有什么关系?

%Nc

的例子有更广泛的覆盖面。)

    

如果我正确理解第 7.21.6.2 节 ¶9 条,则只有 0 长度的输入项才相当于
c floating-point language-lawyer scanf
1个回答
1
投票
输入失败

7.21.6.2 9 并不是唯一指定匹配失败的段落。第 10 段说:

...如果输入项不是匹配序列,则指令执行失败:此条件为匹配失败...

“。”是匹配输入序列的前缀,因此它被扫描(消费,从流中删除),但它不是匹配的输入序列,因此存在匹配失败。

© www.soinside.com 2019 - 2024. All rights reserved.