Ctrl-D(Unix)和Ctrl-Z(Windows)的不同行为

时间:2016-12-19 12:30:16

标签: c windows unix stdin

根据标题,我试图理解 Ctrl + D / Ctrl + Z 的确切行为在一个带有gets的while循环中(我需要使用它)。我正在测试的代码如下:

#include <stdio.h>
#include <stdlib.h>

int main()
{

    char str[80];

    while(printf("Insert string: ") && gets(str) != NULL) {

        puts(str);
    }

    return 0;
}

如果我的输入只是 Ctrl + D (或Windows上的 Ctrl + Z ){{ 1}}返回NULL并且程序正确退出。不清楚的情况是我插入gets(Unix)或house^D^D(Windows)之类的东西。

  1. 在第一种情况下,我的解释是house^Z^Z\n(或getchar函数内部类似的东西)等待read()获取输入,第一个 Ctrl + D 刷新非空的缓冲区(因此不是EOF),然后第二次调用read()被调用EOF被触发。
  2. 在第二种情况下,我注意到第一个 Ctrl + Z 被插入到缓冲区中,而后面的所有内容都被忽略了。因此,我的理解是第一个read()调用插入gets并丢弃其他返回5(读取的字符数)的内容。 (我说5因为否则我认为一个简单的 Ctrl + Z 应该返回1而不触发EOF)。然后程序等待来自用户的更多输入,因此第二次read()调用。
  3. 我想知道我的工作方式是对是错,哪部分只是依赖于实现,如果有的话。

    此外我注意到在Unix和Windows中,即使触发了EOF,它似乎在以下house^Z调用中重置为false,我也不明白为什么会发生这种情况以及代码的哪一行

    我真的很感激任何帮助。

    (2016年12月20日)为了避免混淆,我大量编辑了我的问题

1 个答案:

答案 0 :(得分:2)

CTRL-D和CTRL-Z&#34;文件结尾&#34;指标在Unix和Windows系统上分别起到了类似的作用,但实现方式却完全不同。

在Unix系统(包括像Linux这样的Unix克隆)上,CTRL-D虽然官方描述为文件结束字符,但实际上是一个分隔符。它与用于分隔行的行尾字符(通常是回车符或CTRL-M)几乎完全相同。两个字符都告诉操作系统输入行已完成并使程序可用。唯一的区别是,对于行尾字符,在输入缓冲区的末尾插入换行符(CTRL-J)字符以标记行的结尾,而使用文件结尾字符则不插入任何内容

这意味着当您在Unix上输入house^D^D时,read系统调用将首先返回长度为5的缓冲区,其中包含5个字符house。当再次调用read以获得更多输入时,它将返回长度为0但没有字符的缓冲区。由于在普通文件上读取零长度表示已到达文件末尾,gets库函数也将此解释为文件末尾并停止读取输入。但是,因为它用5个字符填充缓冲区,所以它不会返回NULL以指示它到达文件的末尾。由于它实际上并未实际到达文件末尾,因为终端设备实际上并不是文件,因此在此之后进一步调用gets将进一步调用read,这将返回任何文件用户输入的后续字符。

在Windows上,CTRL-Z的处理方式大不相同。最大的区别在于它根本没有受到操作系统的特殊处理。在Windows上键入house^Z^Z^M时,仅对回车符进行特殊处理。就像在Unix上一样,回车使得类型行可用于程序,但在这种情况下,回车符和换行符被添加到缓冲区以标记行的结尾。因此,结果是ReadFile函数返回一个9字节长的缓冲区,其中包含9个字符house^Z^Z^M^J

它实际上是程序本身,特别是C运行时库,专门处理CTRL-Z。对于Microsoft C运行时库,当它看到ReadFile返回的缓冲区中的CTRL-Z字符时,它将其视为文件结束标记并忽略其后的所有其他内容。使用前一段中的示例,gets最终调用ReadFile以获得更多输入,因为当从控制台(或其他设备)读取时,它看不到CTRL-Z字符的事实)它还没有看到行尾(被忽略)。如果再次按Enter键,gets将返回缓冲区,填充7字节house^Z\0(添加0字节表示字符串结束)。默认情况下,当从普通文件中读取时,如果CTRL-Z字符出现在文件中,它会被忽略后,它会执行相同的操作。这是为了向后兼容CP / M,它只支持长度为128的倍数的文件,并使用CTRL-Z来标记文本文件真正应该结束的位置。

请注意,上述Unix和Windows行为都只是用户输入的正常默认处理。 CTRL-D的Unix处理仅在以规范模式从终端设备读取时才会发生,并且可以更改文件结尾&#34;性格到别的东西。在Windows上,操作系统从不特别处理CTRL-Z,但C运行时库是否执行取决于正在读取的FILE流是处于文本模式还是二进制模式。这就是为什么在便携式程序中,在打开二进制文件时(例如b),你应该始终在模式字符串中包含字符fopen("foo.gif", "rb")