【问题标题】:C - Why does puts behave unexpectedly, but printf does not when using scanf?C - 为什么 puts 的行为出乎意料,但 printf 在使用 scanf 时却没有?
【发布时间】:2021-10-12 17:12:52
【问题描述】:

我之前正在测试一些看起来像这样的代码:

char input;
int data;

while(scanf("%c %d", &input, &data) != EOF) {
    puts("foo");
}

当我使用以下输入阅读它时:

c 8
c 10
c 15
c 18
^D

这是我的输出:

foo
foo
foo
foo
foo
foo
foo
foo

虽然我只希望foo 打印一次;也就是说,一旦发送了 EOF 信号。我还尝试在我的 IDE 之外并在终端中手动运行它(因为 CLion 存在 EOF 问题),当我通过终端手动运行它时, foo 将在第一次输入后打印一次,然后每次都打印两次。

但是,当我在循环中使用 printf 而不是 puts 时,我的代码会按预期运行。我想知道为什么 puts 在这里造成了问题?我想这与 IO 流有关,但我不确定为什么会发生这种情况。

这是未定义的行为吗?如果是这样,为什么会这样?

编辑:cmets 中有人说他们没有遇到同样的问题。我附上了我的代码+终端的屏幕截图。 如果这有什么不同,我也在运行 gcc-9.4。

enter image description here

【问题讨论】:

  • 我无法重现这种行为——printf 对我来说与puts 的行为相同。我看不出为什么它应该这样做;也许不相关的东西出了问题?
  • 请提供minimal reproducible example 来证明您的观察。
  • 我会写while(scanf(" %c%d", &input, &data) == 2) 注意空格字符位置的变化。了解scanf 如何使用不同的格式说明符(在输入中的格式字符串 中)处理空格以及它返回什么是很重要的。
  • 没有“EOF 信号”。当输入流关闭时,scanf 返回一个整数值(几乎可以肯定是 -1),它由令牌 EOF 表示给 C 预处理器,并在文档中称为“EOF”。这不是任何形式的信号。它只是当它正在读取的文件没有更多数据时scanf返回的整数值。
  • scanf 规则一:成功的返回值是成功转换的输入数。所以检查一下(在本例中为 2)——不要只检查 1/0/EOF。 scanf 规则 2:%c 略有不同。要么不要全部使用它,要么总是在它前面加一个空格:" %c %d"。如果您真的知道自己在做什么,请仅使用不带空格的%c(尽管在这种情况下,您不妨只使用getchar)。

标签: c


【解决方案1】:

问题的根源在于%c 没有跳过前导空格。

这是发生了什么 - 您的输入流包含以下序列:

{ 'c', ' ', 8, '\n', 'c', ' ', 10, '\n', 'c', ' ', 15, '\n', 'c', ' ', 18, '\n' }

scanf 的第一次调用将'c' 读入input,将8 读入data 并返回2(用于两次成功的转换和分配)。 next 调用scanf'\n' 读入input尝试'c' 读入data - 这是匹配失败,所以data未分配。但是,由于'\n' 已成功读入inputscanf 返回1(不是EOF)。对scanf 的下一次调用将' ' 读入input10 读入data

起泡、冲洗、重复。根据您在序列中的位置,scanf 将返回210,其中没有一个与EOF 相同。这就是为什么每行会得到多个 foo 输出的原因,因为您没有阅读您认为正在阅读的内容。

你需要做两件事来解决这个问题:

  1. %c 说明符前面放置一个空白 - 这将告诉 scanf 跳过前导空格;

  2. 不是针对 EOF 进行测试,而是针对 2 进行测试 - 您预计每个输入行有 2 次成功的转换和分配:

while ( scanf( " %c %d", &input, &data ) == 2 )
  puts( "foo" );

【讨论】:

    【解决方案2】:

    不要与 EOF 比较,计算有效转化次数。特别是,在您显示的输入中:

    scanf("%c %d", &input, &data) 
    

    会将第一个c读入input,将值8赋给data,然后返回2。输入流中的下一个字符是\n,所以第二个scanf将其存储在@ 987654327@ 并且无法从c 中获取整数值,因此它不会向data 写入任何内容并返回1。对scanf 的下一次调用将c 写入input 并设置data至 10. 冲洗并重复。

    你可能想写:

    while(scanf(" %c %d", &input, &data) == 2) {
    

    (注意%c之前的空格)。

    【讨论】:

      【解决方案3】:

      在第一行之后,"%c %d" 正在消耗前一行的'\n',然后将'c' 阻塞为int,需要另一个循环来处理

      • 使用前导空格来消耗空格。

      • 检查结果。


      // while(scanf("%c %d", &input, &data) != EOF) {
      int count;
      while((count = scanf(" %c %d", &input, &data)) != EOF) {
        printf("count:%d input:%c data:%d\n", 
            count, count >= 1 ? input : '?', count >= 2 ? data : -1);
      }
      

      【讨论】:

        猜你喜欢
        • 2020-09-26
        • 2014-05-30
        • 2011-11-24
        • 1970-01-01
        • 2022-08-02
        • 2011-01-28
        • 1970-01-01
        • 2014-04-02
        • 1970-01-01
        相关资源
        最近更新 更多