【问题标题】:C scanf() function, how to terminate on EOF/EOT?C scanf() 函数,如何在 EOF/EOT 上终止?
【发布时间】:2020-07-05 15:49:22
【问题描述】:

我写了一个简单的程序来测试 C 中的 scanf() 函数。它基本上是从终端读取的,char char重印 return valuechar读;并在遇到 EOF/EOT 或读取 \n newline 时终止。

#include <stdio.h>
#include <stdbool.h>

int main(void) {
    char c; int ret;

    printf("Enter the chars to test: ");

    //LOOP (scan & print) only when return is not EOF and char is not newline
    while ( ((ret = scanf("%c", &c)) != EOF) && c!='\n' ) {
        printf("%i %c\n", ret, c);
    }
    return 0;
}

如果按下换行符 (Enter),它将正确终止。但它不会仅仅以单个Ctrl-D 终止。单个Ctrl-D 将刷新键入的“字符”并打印它们。然后它会再次等待输入,尽管EOF 已与Ctrl-D 一起发送。如果我们在第一次 (2x) 之后直接按第二次 Ctrl-D 或只是 Enter 它将终止。因此,您将需要两个连续的Ctrl-D 来终止程序(或本例中的循环)。

例子:

如果你在终端输入987,然后按Enter;然后 1 91 81 7 将分别打印在换行符上。

如果你在终端输入987,然后按Ctrl-D;那么1 9会打印在同一行(因为输入987后没有输入Enter),1 8, 1 7 将打印在换行符上。然后它仍然会等待更多的输入,除非它通过直接输入第二个连续的 Ctrl-D 或换行符 (Enter) 来终止。所以它(程序)只会在newline 或第二个连续的Ctrl-D 之后停止(退出循环)。

我很困惑。单个Ctrl-D 不应该在这里停止循环吗?收到一个Ctrl-D 后,我应该怎么做才能停止程序(scanf 循环)?

我使用 gcc 9.2.1 在 Lubuntu 19.10 上测试了代码。

【问题讨论】:

  • 我认为这基本上与 stdin 通常是 endline-buffered 的事实有关,如果错了,请纠正我。

标签: c scanf


【解决方案1】:

测试返回值是否为 1,而不是 EOF。当第一次达到 eof 时,返回零。零后的下一个scanf返回EOF

while ( ((ret = scanf("%c", &c)) == 1) && c!='\n' ) {
    printf("%i %c\n", ret, c);
}

此行为是为标准 C 库指定的。

成功时,函数返回参数列表中成功填充的项目数。由于匹配失败、读取错误或到达文件结尾,此计数可能与预期的项目数相符或更少(甚至为零) .

如果发生读取错误或读取时到达文件末尾,则会设置正确的指示符(feof 或 ferror)。

如果您使用更复杂的输入格式,您可以将if (feof(stdin) != 0) break; 添加到循环体中。

如果你只阅读字符,使用fgetc不是更好吗?

【讨论】:

  • 为什么返回0?是错误还是功能?
  • 更好的测试:&lt;= 0!= 1 因为scanf 可以在返回 0 之前返回 EOF。
  • 这对我不起作用(comparing ret != 0)。它仍然需要第二个 Ctrl-D。第一个 Ctrl-D 似乎甚至没有设置返回值。第二个 Ctrl-D 将使其进入无限循环,为“ret”打印 -1,最后一个成功的 char c 读取。
  • 不,==1 也不起作用。 (仍然需要第二个^D)。 Idk 为什么,似乎 scanf 返回值是一个非常棘手的.. =((
【解决方案2】:

问题是scanf() 不会返回EOF,直到没有更多的输入等待并且遇到EOF。 (您的"%c" 转换说明符 将接受任何字符,包括'\n' 字符,视为有效并认为它是成功的转换)

当您键入一行字符时,例如"abcdefg" 并尝试按 Ctrl+d(表示输入结束),输入 ("abcdefg") 被处理,当达到 'g' 时,scanf() 阻塞等待你的下一个输入。 (因为发生了成功的转换,并且没有发生匹配或输入失败)

一旦 Ctrl+d 被第二次键入(表示输入结束),当没有输入要处理时,在第一次成功转换之前达到EOF 并且 输入失败发生,scanf() 然后返回EOF

请参阅:man 3 scanf -- RETURN VALUE sectionC11 Standard - 7.21.6.2 The fscanf function(p16)

【讨论】:

  • 嗯,你的解释很有道理。但我很困惑,我怎么能只用一个 Ctrl-D 来终止程序(scanf 和 printf 循环)。无论如何 Ctrl-D 是传输/输入信号的结束,对吗?那么在您的示例中,在达到“g”之后,scanf() 如何仍然等待下一个输入。我认为这对 C 的设计者来说是相当大的问题..
  • scanf() 块等待输入。当您使用[Ctrl+d] 时,您会生成一个end-of-input(或end-of-transmission),它基本上表示“我已完成输入”。 scanf() 然后尝试处理那里有什么。如果有输入,则由scanf() 处理,您的EOF/EOT 已经完成了它的工作。它告诉scanf()您已完成输入,它应该开始处理。如果没有要处理的输入,那么scanf() 会说“嘿,你说你已经完成了输入并且那里没有输入,所以EOF
  • 另一种方法是将键盘(终端)置于非规范(原始)模式,以便在输入时处理每个字符,而无需等待 '\n'EOT - 然后使用 @ 987654347@,您手动生成的EOF 将被一口气看到。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-05
  • 1970-01-01
  • 2011-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多