实际结束从终端读取需要两个 EOF

Two EOF required to actually end reading from terminal

我一直在努力了解 EOF 的工作原理。在我的代码中(在 Windows 上)调用 EOF (Ctrl+ZEnter) 第一次不起作用,我必须提供两个 EOF 才能真正停止读取输入。此外,第一个 EOF 被读取为一些垃圾字符,当我打印输入时会显示这些字符。 (我们可以在提供的输出中看到最后显示的垃圾字符)。

这是我的代码:-

#include<stdio.h>

#define Max 1000

int main()
{
    char c, text[Max];
    int i = 0;

    while((c = getchar()) != EOF)
    {
        text[i] = c;
        i++;
    }

    printf("\nEntered Text: \n");
    puts(text);

    return 0;
}

我的输出:

我有这样的疑问:-

为什么需要两个 EOF?以及如何防止第一个被读取(作为一些垃圾)并存储为我输入的一部分?

尝试将 c 的类型更改为 int,因为 EOF 可以是负数,通常定义为 -1char 可能无法存储 -1。另外,不要忘记在将字符串传递给 puts 之前以 [=16=] 结尾。

Control-Z 仅在新行的开头被识别为 EOF。因此,如果你想在一条线的中间检测它,你需要自己做。

因此更改此行:

while((c = getchar()) != EOF)

对此:

while((c = getchar()) != EOF && c != CTRL_Z)

然后添加:

#define CTRL_Z ('Z' & 0x1f)

在程序的顶部。

您可能还需要在 Ctrl-z[=29 之后输入 return =] 来让程序读取缓冲输入,但它应该丢弃 ^Z.

之后的所有内容

以下解决方案修复了 Ctrl+Z 问题和垃圾输出,还阻止了缓冲区溢出。我评论了更改:

#include <stdio.h>

#define Max 1000
#define CTRL_Z 26           // Ctrl+Z is ASCII/ANSI 26

int main()
{
    int c ;                  // getchar() returns int
    char text[Max + 1] ;     // +1 to acommodate terminating nul
    int i = 0;

    while( i < Max &&                 // Bounds check
           (c = getchar()) != EOF && 
           c != CTRL_Z )              // Check for ^Z when not start of input buffer
    {
        text[i] = c;
        i++;
    }

    text[i] = 0 ;        // Terminate string after last added character

    printf( "\nEntered Text:\n" );
    puts( text );

    return 0;
}

这种行为的原因有点神秘,但文件结束与 Ctrl-Z 不同。当且仅当控制台输入缓冲区为空时,控制台生成导致 getchar() 到 return EOF (-1) 的文件结尾,否则它会将 ASCII SUB (26) 字符插入流中. SUB 的使用最初是为了与更早的 CP/M 操作系统兼容 MS-DOS。特别是 CP/M 文件由固定长度的记录组成,因此记录中间的 ^Z 用于指示不是记录长度的精确倍数的文件的有效数据结束。在控制台中,如果 SUB 不在输入缓冲区的开头并且 SUB 之后的所有字符都被丢弃,则 SUB 是可读的而不是生成 EOF。都是回来后的一团乱麻。

Windows 终端在键盘输入中(至少在默认配置中)遵循的关于 ^Z 的逻辑如下:

  • Ctrl-Z 组合本身不会导致输入行缓冲区被推送到等待的应用程序。此组合键只是在输入缓冲区中生成 ^Z 个字符。您必须按 Enter 完成该行缓冲区并将其发送到应用程序。

    您实际上可以在 ^Z 之后和按 Enter 之前继续输入其他字符。

  • 如果输入行不是以 ^Z 开头,而是在内部包含 ^Z,则应用程序将收到该行直到 并包括 第一个 ^Z 字符(读作 \x1A 字符)。其余的输入被丢弃。

    例如如果你输入

    Hello^Z World^Z123
    

    然后按 输入 你的 C 程序实际上会读取 Hello\x1A 序列。 EOF条件不会出现。

  • 如果输入行以^Z开头,则整行被丢弃并设置EOF条件。

    例如如果你输入

    ^ZHello World
    

    然后按 Enter 您的程序将不读取任何内容并立即检测 EOF。

这是您在实验中观察到的行为。请记住,getchar() 的结果应该接收到 int 变量,而不是 char 变量。