问题 为什么EOF字符如果放在一行末尾不起作用?


我正在学习C ++并试图理解为什么EOF字符(Windows上的Ctrl + Z)如果放在一行的末尾就不会破坏while循环。

我的代码:

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

当我输入^ Z时,循环中断。但是当我输入12 ^ Z时,它没有。为什么?


7493
2017-07-07 21:48


起源

可能与12 ^ z!= ^ z有关... 12 ^ z将不会评估为false - Mare Infinitus
Unix系统的工作方式相同;一行中间的CTRL-D被忽略(或者可能不完全; bash shell会发出哔哔声,但仍然会忽略它),它只能在一行的开头工作。我不知道是否有一个真正的原因,或者是否有人回到60年代,当Unix被发明时认为这将是一件好事,而且从那时起就一直没有人知道为什么:-) - Christian Stieber
@ChristianStieber:在类Unix系统上,单个control-D在一行开头触发文件结束条件;除此以外, 二 control-Ds触发文件结束条件。 - Keith Thompson


答案:


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

这意味着在Windows中,无法编写可将其输入精确复制到其输入的可移植C或C ++程序。

在Unix-land中,这根本不是问题。

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

我不确定CP / M从哪里得到它,但它只是 类似,完全不一样!,作为Unix'[Ctrl D]。

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

主要区别在于Windows内部文件标记的文本末尾 数据,这可能发生在一个文件中,而在Unix内部则是这样 缺乏数据,不能在文件中出现。当然,Windows还支持文本的普通文件结尾(不再有数据!)。这使事情变得复杂 - Windows更复杂。


#include <iostream>
using namespace std;

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

3
2017-07-07 22:25



我仍然不明白的是,在某些时候,当我的代码中的1和2被读取并放入ch时,它们应该从cin中消失,因此剩下的唯一字符是[Ctrl + Z] ,就好像它是由单个EOF组成的一条线。然后cin.eof()应该返回true。 - Cutter
输入中的[Ctrl Z],单独在一行,是一回事。在Windows中,它将被翻译。在文本翻译中幸存下来的[Ctrl Z]是一个非常不同的东西。试试我现在添加到答案中的代码。 - Cheers and hth. - Alf


您将无法在C ++标准中找到问题的答案。

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

Windows使用Control-Z触发文本输入流的文件结束条件;除了在一行的开头,它恰好不会这样做。

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

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

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


7
2017-07-07 22:25





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

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

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

您可能也对此SO感兴趣:

所以关于标志的语义


1
2017-07-07 21:59



谢谢你的澄清。但是,据我所知,当一个角色投入使用时 ch,它从cin中删除。因此,在将1和2放入ch之后,只有^ Z保留在cin中并且cin.eof()应该返回true。循环应该打破,是吗? - Cutter
输入的字符串不是按顺序计算的。它们被计算为单个输入。并且只有当此输入是eof时,才将eof设置为true。 - Mare Infinitus
但是,当读取其他所有字符(在EOF之前)时,为什么不将eof()设置为true? - Cutter
正如我所说,输入不是按顺序读取,而是按块方式读取 - Mare Infinitus
如果这解释了什么,我有疑问。即使它可以解析有效的输入值,它仍然应该触发 eof  后 不,不应该吗?并且块根本不需要与换行符连接,尽管在实践中它们通常是。 - leftaroundabout