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

标签 c windows unix stdin

按照标题,我试图用gets(需要使用)在while循环中了解Ctrl + D/Ctrl + Z的确切行为。我正在测试的代码如下:

#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),则gets返回NULL,并且程序正确退出。目前尚不清楚的情况是,当我插入house^D^D(Unix)或house^Z^Z\n(Windows)之类的东西时。
  • 在第一种情况下,我的解释是getchar(或gets函数内部类似的东西)等待read()获得输入,第一个Ctrl + D刷新不为空的缓冲区(因此不是EOF),然后第二次read()称为EOF被触发。
  • 在第二种情况下,我注意到第一个Ctrl + Z插入了缓冲区,而后面的所有内容都被忽略了。因此,我的理解是第一个read()调用插入了house^Z并丢弃了其他所有内容,并返回5(读取的字符数)。 (我说5是因为否则我认为简单的Ctrl + Z应该返回1而不会触发EOF)。然后,程序等待来自用户的更多输入,因此进行了第二次read()调用。

  • 我想知道我对它的工作方式是对还是错,它的哪一部分只是依赖于实现(如果有)。

    此外,我注意到在Unix和Windows中,即使在触发EOF之后,在随后的gets()调用中它似乎也重置为false,我不明白为什么会发生这种情况以及在代码的哪一行。

    我将非常感谢您提供的任何帮助。

    (12/20/2016)为了避免造成困惑,我对问题进行了严格的编辑

    最佳答案

    CTRL-D和CTRL-Z“文件结束”指示符分别在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向后兼容,CP/M仅支持长度为128的倍数的文件,并使用CTRL-Z标记文本文件实际上应该在何处结束。

    请注意,上述Unix和Windows行为都是用户输入的常规默认处理。 Unix对CTRL-D的处理仅在以规范模式从终端设备读取时发生,并且可以将“文件结尾”字符更改为其他字符。在Windows上,操作系统从不对CTRL-Z进行特殊处理,但是C运行时库是否进行处理取决于所读取的FILE流是文本模式还是二进制模式。这就是为什么在可移植程序中打开二进制文件(例如b)时,应始终在模式字符串中包括字符fopen("foo.gif", "rb")的原因。

    关于c - Ctrl-D(Unix)和Ctrl-Z(Windows)的不同行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41222635/

    相关文章:

    UNIX 进程 : fork() and wait()

    c - 输入值和 If 语句

    C 内联源文件中的所有函数

    c - 取消引用的 union 成员字节不相同

    c - 在 C-windows 中截图

    windows - 我如何在某些 'if' 语句中使用 Windows 版本标题来确定要检查哪些 Windows 更新?

    c - Transmission-daemon 只是停止通量

    使用指针反转字符串的 C 代码

    windows - GIT:不处理包含 unicode 字符的文件名(例如中文/韩文)

    c++ - 将 ntp 时间设置为特定时区