为什么需要多个EOFCTRL + Z字符?

问题描述

| 作为一点背景,我对C编程语言还是很陌生,因此一直在尝试通过Kernighan&Ritchie手册第二版中的一些练习。我确实意识到我可以通过更多地使用标准库来更简洁地处理某些问题,但是我试图使有用的命令与本书尽可能保持同步。 如果有什么不同,我将使用Tiny C编译器(TCC)在Windows XP环境中编译源代码,并在XP控制台(cmd.exe)中执行二进制文件。 问题:处理
End-of-File (EOF) characters
。我整理了一个小的测试用例来说明问题。该程序似乎可以处理EOF字符(部分)。我将尝试通过示例输入/输出来演示该问题。
#include <stdio.h>

int main() 
{
    int character,count;

    character = 0;
    character = getchar();

    for (count = 0; character != EOF; ++count) 
    {
        character = getchar();
    }

    printf(\"Count: %d\",count);
    return 0;
}
示例输入1:1:2ѭ(其中^ Z / CTRL + Z代表EOF字符,[enter]代表Enter键。) 样本输出1:
Count: 4
(等待更多输入或以^ C / ^ Z [enter]正确结束) 样本输入2:
abcd^zefgh
样本输出2:
Count: 4
(等待更多输入或以^ C / ^ Z [enter]正确结束) 如在两个示例中所述,直到启动^ C / ^ Z [enter]序列后,才输出字符计数。在启动之前,程序将等待(甚至处理)更多的输入。但是,如示例2所示,当程序遇到初始^ Z时,如果启动^ C / ^ Z [enter]序列,它将停止处理该行输入,等待更多输入或返回正确的计数。 我无法弄清楚为什么该程序仅部分处理EOF字符。在我看来,如果要截断样本2的末尾,它也应该完全脱离循环。有什么想法为什么在识别EOF字符后程序不会立即打印当前计数并退出?     

解决方法

这个答案是unix-ish,但是我认为Windows上也发生了类似的现象。 EOF的基本形式是零长度
read
。在交互式输入设备(终端)上,有一种特殊的机制可以在输入流中包含EOF,但是如果已经有要读取的输入,它将与该输入一起被消耗(导致非零长度
read
),因此应用程序永远不会注意到它。只有当EOF发生且没有先前的输入缓冲时,应用程序才能注意到它并采取措施。 如果您可以访问Linux(或其他* nix)系统,请编写类似的测试程序并在
strace
下运行它。观察潜在的“ 6”呼叫发生的原因,这种原本不直观的行为的原因将是有道理的。     ,这可以追溯到计算的基石时代。至少为CP / M,早期的DEC操作系统可能更长。 CP / M不存储文件的大小,它仅记录磁盘扇区的数量,每个扇区128个字节。对于二进制文件而言,这不是问题,只要程序有足够的空间,它就会停止读取。但是对于文本文件肯定是一个问题。 因此,按照惯例,文本文件的文件结尾标记为代码0x1a,即Control + Z。由于文本文件的遗留量大于其中的文本量,因此必须在每一代连续的CRT实现中都保留下来。 Windows并没有为此提供任何帮助,这纯粹是CRT实现的细节。这就是为什么在控制台上键入Ctrl + Z并不会执行任何特殊操作的原因。按下Enter键后,cmd.exe中的CRT再次调用旧版行为并声明EOF。     ,我不确定是否使用TCC,但是在很多(大多数?)情况下,您需要或多或少单独输入^ Z才能将其识别为EOF(即,您需要输入[输入] ^ z [enter])。     ,当您键入^ Z时,Windows不会自动生成EOF。这只是从DOS继承下来的约定。 C编译器的运行时必须识别它并设置EOF标志,我猜想Tiny C不会这样做。 另一方面,Windows命令环境可以识别^ C。它不一定表示EOF,我认为它更多是中止信号。     ,我猜标准输入是行缓冲的(在Unix上)。 DOS有一些
getch()
getche()
函数,它们的功能低于stdio,因此它们绕过了stdio缓冲。我不知道如何在Windows上禁用输入缓冲,在Unix上是通过将终端设置为非规范模式来完成的。