保证 getchar 收到换行符或 EOF(最终)?

Guarantee that getchar receives newline or EOF (eventually)?

提问人:Ana Nimbus 提问时间:9/5/2020 更新时间:9/5/2020 访问量:84

问:

我想从以下情况之一开始阅读字符:stdin

  • 遇到行尾标记(在我看来是正常情况),
  • 这种情况发生,或EOF
  • 发生错误。

我如何保证上述事件之一最终会发生?换句话说,我如何保证最终将返回 or ,前提是没有发生错误(就 而言)?getchar\nEOFferror(stdin)

// (How) can we guarantee that the LABEL'ed statement will be reached?
int done = 0;
while (!0) if (
        (c = getchar()) == EOF || ferror(stdin) || c == '\n') break;
LABEL: done = !0;

如果连接到始终提供除 以外的字符的设备,则不会发生上述任何情况。答案似乎与设备的属性有关。在哪里可以找到这些详细信息(也许在编译器、设备固件或设备硬件的配置中)?stdin'\n'

特别是,我想知道键盘输入是否保证由行尾标记或文件末尾条件终止。同样,对于存储在光盘/ SSD上的文件。

典型用例:用户在键盘上输入文本。程序读取前几个字符并丢弃所有剩余字符,直到行尾标记或文件末尾(因为某些缓冲区已满,或者之后一切都是注释等)。

我正在使用 C89,但我很好奇答案是否取决于使用哪种 C 标准。

C 换行符 stdin eof getchar

评论

1赞 Shawn 9/5/2020
由于标准输入来自外部源(用户、其他程序等),因此很难 100% 确定您会从恶意用户那里获得换行符或 EOF。 例如,只会给它一个无穷无尽的 X 流。while true; do printf x; done | ./yourprogram
0赞 Ana Nimbus 9/5/2020
正如我所怀疑的那样,@Shawn,这个问题超出了 C 的范围。我想在实践中,需要近似于所需效果的程序会使用某种包装器,例如,在读取这么多次后退出。stdin

答:

2赞 user253751 9/5/2020 #1

你不能。

假设我运行你的程序,然后我在键盘的“X”键上放了一个重物,然后去夏威夷度假。在去那里的路上,我被闪电击中而死。

除了“x”之外,永远不会有任何输入。

或者,我可能会决定输入《白鲸》的完整故事,而不按回车键。这可能需要几天时间。你的程序应该等多久才能决定我可能永远打不完?

希望它做什么?

评论

0赞 Ana Nimbus 9/5/2020
看起来明显的折衷方案是限制返回的顺序非 - 的数量。还有其他明显的妥协吗?\n
0赞 user253751 9/5/2020
@AnaNimbus 如果我在“X”键上加重并去度假,您希望您的程序做什么?
0赞 Ana Nimbus 9/5/2020
我猜你的意思是,一般的解决方案是不可能的,所以我需要考虑每个应用程序。在您的示例中,如果发生此类事件,我想要求键盘(或键盘设备驱动程序)设置读取错误标志。我知道---那么我们必须定义“这样的事件”---也许.也许这正在变成一个用户界面问题---关于“典型用户”期望(或将容忍)什么。此 StackOverflow 输入字段将只接受这么多字符。有限的字符数似乎是大多数用户应该容忍的事情。keydown time > vacation duration
1赞 user253751 9/5/2020
@AnaNimbus 是的,这是一个关于您希望用户界面做什么的问题。我认为最明智的做法是让计算机完全按照你的吩咐去做。如果计算机忽略您键入的内容,而您继续键入,计算机将继续忽略它,而不是弹出一条消息说“嘿!我忽略了很多文字!你确定你不是故意按回车键的吗?像后者这样的东西可能适合针对儿童或没有计算机经验的成年人,但我没想到会这样。
2赞 Keith Thompson 9/5/2020
@AnaNimbus“我想你说的是不可能有一个普遍的解决方案”,我认为问题是你还没有定义问题是什么。你的问题“我怎么能保证上述事件之一最终会发生?”的答案是“你不能”。你试图弄清楚该怎么做,因为你问的保证是不可能的——也许有无数种可能的答案。您的要求是什么?
1赞 Lev M. 9/5/2020 #2

看看评论中的所有讨论,似乎你找错了地方:

这不是键盘驱动程序或包装的问题。stdin

这也不是你使用什么编程语言的问题。

这与软件中输入的目的有关。
基本上,作为程序员,你要知道你想要或需要多少输入,然后决定何时停止读取输入,即使有效的输入仍然可用。

请注意,不仅有些设备可以在不触发 EOF 或行尾条件的情况下永久发送输入,而且有些程序可以永远愉快地读取输入。

这是设计使然。

常见示例可以在 POSIX 风格的 OS(如 Linux)命令行工具中找到。 下面是一个简单的示例:

cat /dev/urandom | hexdump

只要您的计算机正在运行,这将打印随机数,或者直到您点击Ctrl+C

虽然当没有更多要打印的东西(EOF或任何读取错误)时会停止工作,但它并不期望这样的结束,所以除非你正在使用的实现中有一个错误,否则它应该永远快乐地运行。cat

所以真正的问题是: 您的程序何时需要停止读取字符,为什么?

1赞 John Bollinger 9/5/2020 #3

如果 stdin 连接到始终提供除“\n”以外的字符的设备,则不会出现上述任何情况。

例如,设备。是的,stdin 可以连接到从不提供换行符或到达 EOF 的设备,并且预计不会报告错误情况。/dev/zero

答案似乎与设备的属性有关。

确实如此。

在哪里可以找到这些详细信息(也许在编译器、设备固件或设备硬件的配置中)?

通常,这是设备驱动程序的问题。在某些情况下(例如示例),无论如何,这就是全部。通常,驱动程序会执行对底层硬件有意义的操作,但原则上,它们不必执行。/dev/zero

特别是,我想知道键盘输入是否保证由行尾标记或文件末尾条件终止。

不。一般来说,当且仅当按下 <enter> 键时,终端设备才会发送行尾标记。如果终端断开连接(但程序继续),或者如果用户明确导致发送文件结束条件(例如,在 Linux 或 Mac 上键入 <-<D>,在 Windows 上键入 <-<Z>),则可能会发出文件结束条件的信号。这些事件实际上都不需要在任何给定的程序运行中发生,而后者不这样做是很常见的。

同样,对于存储在光盘/ SSD上的文件。

通常,您可以依靠从普通文件中读取的数据来包含文件本身中存在的换行符。如果文件以文本模式打开,则特定于系统的文本行终止符也将转换为换行符(如果不同)。文件不必包含其中任何一个,因此从常规文件中读取的程序可能永远不会看到换行符。

当文件位置等于或超过文件数据的 and 时,尝试读取时,您可以依赖 EOF 发出信号。

典型用例:用户在键盘上输入文本。程序读取前几个字符并丢弃所有剩余字符,直到行尾标记或文件末尾(因为某些缓冲区已满,或者之后一切都是注释等)。

我觉得你太努力了。

在某些情况下,读到行尾可能是一件合理的事情。如果程序旨在支持交互式使用,则期望最终达到换行符是合理的。但是,试图确保无效数据无法馈送到您的程序是一个失败的原因。你的目标应该是接受尽可能广泛的输入,并在出现其他输入时优雅地失败。

如果您需要以逐行模式读取输入,那么请务必这样做,并记录您这样做。如果只有每行的前 n 个字符对程序很重要,那么也要记录下来。然后,如果当用户将其输入连接到他们而不是您时,您的程序永远不会终止。/dev/zero

另一方面,尽量避免放置任意限制,尤其是对事物的大小。如果对某物的大小没有自然限制,那么你引入的人为限制就足够了。

评论

0赞 Ana Nimbus 9/5/2020
+1.有很多好事要考虑。在许多实际情况下,可用内存或可用存储(也许是可用时间?)似乎是“自然限制”的合理定义。例如,“......'咕噜咕噜'......除了可用内存之外,对输入行长度没有限制“(GNU grep 3.0 信息页面,强调后加)。
0赞 John Bollinger 9/5/2020
@AnaNimbus,我对自然限制的想法更像是程序必须遵守的外部指定格式。这些限制与系统资源限制之间的区别在于,前者是一个固定的限制,您可以预先知道并对其进行编程,而对于后者,您通常不会提前知道限制,而是根据需要使用尽可能多的限制,直到系统不会(或不能)为您提供更多。在处理函数调用的返回值时,良好的纪律通常会在函数调用用完时提供正常失败。
2赞 Lev M. 9/5/2020
@AnaNimbus请注意,仅当您打算存储输入时,内存才是一个限制因素。如果只处理它然后丢弃它,例如在哈希算法中,那么内存就不是一个约束。