【问题标题】:Why doesn't the EOF character work if put at the end of a line?如果放在行尾,为什么 EOF 字符不起作用?
【发布时间】:2012-07-07 21:48:27
【问题描述】:

我正在学习 C++ 并试图理解,
如果将 EOF 字符(Windows 上的 Ctrl + Z)放在行尾,为什么不会中断 while 循环?

我的代码:

    int main() {
        char ch;
        while(cin >> ch) {
            cout << ch;
        }
    }

当我输入 ^Z 时,循环中断;
但是当我输入 12^Z 时,它没有。

【问题讨论】:

  • 可能与 12^z != ^z ... 12^z 不会评估为假
  • Unix 系统的工作方式相同;行中间的 CTRL-D 被忽略(或者可能不完全;bash shell 会发出哔哔声,但仍然忽略它),它只在行首起作用。我不知道这是否有真正的原因,或者 60 年代 Unix 发明时的某个人是否认为拥有这将是一件好事,从那时起就一直没有人知道为什么:-)
  • @ChristianStieber:在类 Unix 系统上,单个 control-D 在行首触发文件结束条件;否则,两个 control-D 触发文件结束条件。

标签: c++ windows stream event-handling eof


【解决方案1】:

C 和 C++ 标准允许文本流在默认的 文本模式 下做一些非常邪恶的事情。这些 Unholy Things 包括内部换行标记和外部换行控制字符之间的转换,以及将某些字符或字符序列视为表示 文件结束。在 Unix 领域它没有完成,但在 Windows 领域它已经完成,所以代码只能与原始 Unix 领域的约定相关。

这意味着在 Windows 中,没有办法编写可移植的 C 或 C++ 程序,将其输入完全复制到其输入。

在 Unix 领域,这完全没有问题。

在 Windows 中,由单个 [Ctrl Z] 组成的行按照惯例是文件结束标记。不仅在控制台中如此,在文本文件中也是如此(取决于工具)。 Windows 继承了 DOS 的这一点,后者又继承了 CP/M 的总体思想。

我不确定 CP/M 从哪里得到它,但它只是 类似,根本不一样!,就像 Unix' [Ctrl D]。

在 Unix 领域,文件结尾的一般约定只是“不再有数据”。默认情况下,在控制台中,[Ctrl D] 会将您输入的文本立即发送到等待程序。当您还没有在该行上键入任何内容时,将发送 0 个字节,并且按照惯例返回 0 个字节的读取会遇到文件结尾。

主要区别在于,在 Windows 内部,文件末尾的文本标记是 data,它可以出现在文件中,而在 Unix 内部,它是 缺少数据,这不能在文件中发生。当然,Windows 也支持文本的普通文件结尾(没有更多数据!)。这让事情变得复杂了——Windows 更复杂。


#include <iostream>
using namespace std;

int main()
{
    char ch;
    while(cin >> ch) {
        cout << 0+ch << " '" << ch << "'" << endl;
    }
}

【讨论】:

  • 我仍然不明白的是,在某些时候,当我的代码中的 1 和 2 被读取并放入 ch 时,它们应该从 cin 中消失,因此只有剩余的字符是 [Ctrl + Z],就像它是由单个 EOF 组成的行一样。然后 cin.eof() 应该返回 true。
  • [Ctrl Z] 在输入中,单独一行,是一回事。在 Windows 中,它将被翻译。在文本翻译中幸存下来的 [Ctrl Z] 是完全不同的东西。试试我现在添加到答案中的代码。
【解决方案2】:

您不会在 C++ 标准中找到问题的答案。

cin &gt;&gt; ch 将是一个“真”条件,只要既没有文件结束条件也没有输入错误。文件结束条件的触发方式不是由语言指定的,它可以并且会因操作系统而异,甚至在同一操作系统中具有配置选项。 (例如,类 Unix 系统默认使用 control-D,但可以通过 stty 命令更改。)

Windows 使用 Control-Z 触发文本输入流的文件结束条件;除了在行首之外,它只是碰巧没有这样做。

Unix 的行为有点不同;它在一行的开头使用 Control-D(默认情况下),或在一行中间使用 两个 Control-D。

对于 Unix,这仅适用于从终端读取;如果您正在从文件中读取,control-D 只是另一个非打印字符,它不会触发文件结束条件。即使从磁盘文件中读取,Windows 似乎也将 control-Z 识别为文件结束触发器。

底线:不同的操作系统行为不同,主要是出于模糊的历史原因。 C++ 旨在处理任何这些行为,这就是它没有具体说明某些细节的原因。

【讨论】:

    【解决方案3】:

    这是由 cin 引起的 >> ^Z 将评估为 false。

    更详细:cin.eof() 将返回 true,因此 隐式调用 eof() 的 while 将返回 false 并因此结束循环。

    如果输入 12^Z,eof() 将返回 false,因为它可以解析 一个有效的输入值,因此它不会停止循环。

    您可能也对这个 SO 感兴趣:

    SO on semantics of flags

    【讨论】:

    • 感谢您的澄清。但是,据我了解,当将一个字符放入ch 时,它会从cin 中删除。因此,将 1 和 2 放入 ch 后,只有 ^Z 保留在 cin 中,并且 cin.eof() 应该返回 true。那么循环应该会中断,对吗?
    • 输入的字符串不是按顺序计算的 afaik。它们被计算为单个输入。并且仅当此输入是 eof 时,才将 eof 设置为 true。
    • 但是为什么在读取所有其他字符(在 EOF 之前)时没有将 eof() 设置为 true?
    • 正如我所说,输入不是按顺序读取的,而是按块读取的 afaik
    • 我怀疑这是否能解释什么。即使它可以解析一个有效的输入值,它仍然应该触发eof after,不是吗?并且块根本不需要与换行符连接,尽管实际上它们通常是。
    猜你喜欢
    • 2016-11-30
    • 2014-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    相关资源
    最近更新 更多