当第一个格式字符串字符不是空格时 scanf 的行为

Behavior of scanf when first format string character is not whitespace

提问人:Amittai Aviram 提问时间:10/22/2023 最后编辑:OkaAmittai Aviram 更新时间:10/24/2023 访问量:100

问:

scanf应该使用和丢弃格式字符串中不属于转换说明符的任何字符。但是,当非空格非转换字符出现在格式字符串的第一位时,它的行为似乎有所不同。为什么?

int main() {
    int num, num2;
    printf("> ");
    while (scanf("> %d %d", &num, &num2)) {
        printf("You entered the number %d %d.\n", num, num2);
        printf("> ");
    }
    return EXIT_SUCCESS;
}

如果构建并运行此命令,然后输入

> 3 4

在出现提示时,它会打印消息和重复的提示,然后立即退出。

因此,这意味着第一次返回,然后在用户输入另一组令牌之前返回。如果从格式字符串中删除 ,则循环将一直运行,直到用户输入非数字的内容,然后导致返回 - 这是我所期望的行为。scanf20> scanf0

此外,如果我在第一个转换说明符之后放置相同的符号,循环将继续按预期运行。也就是说,如果格式字符串有 , 比如说, ,并且用户输入"%d > %d"

3 > 4

循环将再次运行并接受另一轮输入。

我没有看到任何关于此行为的文档。

c scanf 格式字符串

评论

0赞 tadman 10/22/2023
而不是,为什么不用指针遍历字符串,然后自己弄清楚呢?弄清楚像这样的简单语法几乎是微不足道的。scanf
3赞 Weather Vane 10/22/2023
在第二个循环中,您要求它与输入中的换行符重新挖掘匹配,因此它失败了。'>'
1赞 David C. Rankin 10/22/2023
用于避免该问题。您的读循环测试必须是 否则,如果输入手动 EOF,它将失败(非常严重)。(有关正确的解决方案@pmg请参阅下面的评论)将其全部读入缓冲区,然后调用缓冲区,而不是直接读取。" > %d %d"while (scanf("> %d %d", &num, &num2) == 2)sscanf()scanf()
1赞 pmg 10/22/2023
不相关:不用于用户输入。 不是为用户输入而设计的。请改用。scanf()scanf()fgets()
2赞 David C. Rankin 10/22/2023
我认为这就是为什么他以......底线是您按以下输入生成的。 不是转换说明符,并且无法读取/丢弃空格。在前面添加空格允许丢弃零个或多个空格。未检查会邀请未定义的行为。他的观点是使用,足够大的缓冲区完全避免了使用中的陷阱。"unrealated:"'\n'[Enter]'>'== 2fgets()scanf()

答:

5赞 Oka 10/22/2023 #1

从一些文档中:fscanf

格式字符串包括

  • 非空格多字节字符,但以下情况除外:格式字符串中的每个此类字符都只使用输入流中的一个相同字符,或者如果流上的下一个字符不相等,则会导致函数失败。%

虽然说明符使用任何和所有1 个前导空格,但它不会使用它后面的换行符,并且在后续迭代中与该换行符 () 不完全匹配fscanf%d'>''\n'

来自同一文档:

  • 空格字符:格式字符串中的任何单个空格字符都会使用输入中所有可用的连续空格字符(就像通过在循环中调用 IsSpace 来确定一样)。请注意,格式字符串中的 、 、 或其他空格之间没有区别。"\n"" ""\t\t"

因此,格式说明符中的前导空格将使用输入中的尾随换行符:

#include <stdio.h>

int main(void)
{
    int num, num2;

    while (1) {
        printf("Enter \"> NUM NUM2\": ");

        if (2 != scanf(" > %d %d", &num, &num2))
            break;

        printf("You entered the number %d %d.\n", num, num2);
    }
}
Enter "> NUM NUM2": > 1 2
You entered the number 1 2.
Enter "> NUM NUM2": > 3 4
You entered the number 3 4.

顺便说一句:这将永远循环在 的真实返回值上(负数,几乎普遍):EOFint-1

while (scanf("> %d %d", &num, &num2))

您应该显式检查 的返回值是否为转换说明符的预期数量(即 )。scanf2


1. %c、%[ 和 %n 之外的所有格式说明符也是如此(假设未发生错误)。

评论

1赞 David C. Rankin 10/22/2023
值得解释的是,除 、(和 ) 之外的所有转换说明符都丢弃了前导空格。可能还想解释为什么会起作用。"%c""%[..]""%n"" > %d %d"
0赞 Amittai Aviram 10/22/2023
谢谢。我遗漏的一点是如何在转换说明符而不是文字字符之前丢弃前导空格。检查实际返回值的要点很好——我想让我的示例保持简单,以说明具体问题。
0赞 Oka 10/22/2023
@DavidC.Rankin现在应该被覆盖了,干杯。
0赞 David C. Rankin 10/22/2023
是的,你明白了,点了点头。
0赞 Oka 10/22/2023
不过,@AmittaiAviram Correct 很简单!更少的错误意味着需要关注的更少。也就是说,在使用时很难正确这一事实是为什么这么多人会建议你避免使用该函数(通常支持基于行的处理)——即使它的行为可以被描述。scanf
2赞 anastaciu 10/22/2023 #2

我大体上同意所有的评论,尤其是那些强调用户输入并不是一个好主意的评论。scanf

你的问题的答案也大多是给出的,在第一个输入中,一个尾随的换行符“字符”被添加到输入流中,它不会被解析并保留在那里,在第二个循环中,换行符将与说明符中的第一个字符匹配,在这种情况下,它将无法匹配, 返回 0,打破循环。Enterscanf>scanf

正如建议的那样,在说明符中添加前导空格将强制使用所述换行符并清除输入缓冲区,该缓冲区将再次等待输入,从而暴露此行为。scanf