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

Two EOF required to actually end reading from terminal

提问人:abhinavk 提问时间:8/12/2018 最后编辑:Studentabhinavk 更新时间:8/13/2018 访问量:331

问:

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

这是我的代码:-

#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;
}

我的输出:

My Output:

我有这个疑问:-

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

C Windows 输入 EOF

评论

0赞 WhozCraig 8/12/2018
仅供参考,在将字符串发送到 之前,您不会终止字符串,因此会调用未定义的行为。这比任何事情都重要,这是您在打印时获得“一些垃圾字符”的原因。在 while 循环关闭应该有一个 IMMEDIATELY。putstext[i] = 0;
0赞 dgnuff 8/12/2018
您是否尝试过在新行上输入第一个 ^Z,而不是紧跟在“?”之后?另外,当您在调试器中单步执行此操作时会发生什么?观察 in 中的值可能会提供有关正在发生的事情的线索。c
0赞 abhinavk 8/12/2018
@WhozCraig我尝试进行您建议的更改,但仍然无法解决问题。无论如何,感谢您指出它。
1赞 WhozCraig 8/12/2018
请原谅我,但我根本不相信特定的更改不能解决“一些垃圾字符”的问题。关于需要两个 ctrl-Z 来模拟 EOF 的操作,如果您搜索它们,该主题将在本网站上被多个问题所涵盖。对于非空输入行,第一个 ctrl-Z 是刷新当前行缓冲区,第二个是跳闸 EOF。对于空白的换行符,只需输入一次。
1赞 WhozCraig 8/12/2018
@Clifford第一个 ctrl-Z 不会被忽略,则在输入后输入时会刷新当前行,但不会刷新紧接前面的行(通常是 CR、LF,两者兼而有之,具体取决于系统)。如果输入后跟没有回车/回车,则需要 ctrl-Z 两次;一个用于刷新线(没有换行符尾随),一次用于点亮 EOF。如果完成,那么没有别的,只需要一个 ctrl-z。Hello WorldHello World <enter>

答:

1赞 Giedrius Statkevičius 8/12/2018 #1

尝试将 的类型更改为 as 可以是负数,通常将其定义为 。 可能存储也可能无法存储 。另外,在将字符串传递给 之前,不要忘记以 结束字符串。cintEOF-1char-1\0puts

评论

0赞 WhozCraig 8/12/2018
我完全同意这一点。 返回 ,并且所述相同的受体也应该是相同的。它不能解决其他问题,也不能回答问题,但同样值得注意。getchar()int
0赞 Clifford 8/12/2018
答案是“试试......”不是答案 - 除非你知道它有效。我测试了它,它没有解决问题。该点是有效的,返回一个 int 和 EOF 不能用 表示,但这只值得注释,而不是答案。getchar()char
0赞 Giedrius Statkevičius 8/12/2018
问题是为什么需要两个 EOF - 答案是它们不是。而且它肯定不会存储在数组中。通过这些更改,IMO 它是一个完全有效的 C 代码(直到),所以它超出了 C 的领域,而是一个关于 ^D 或平台特性的问题。texti < Max
0赞 Clifford 8/12/2018
@GiedriusStatkevičius :Ctrl+Z 存储在输入缓冲区中,如果 ^Z 不在输入缓冲区的开头,则以 ASCII 26 (SUB) 的形式返回。的确,EOF不是缓冲的,而是生成的。关键是您提出了一个未经测试的解决方案,但它不起作用 - 我知道,因为我测试过它。这是特定于 Windows(以及历史上的 MS-DOS)的,并且有些晦涩难懂。^D 是 POSIX 控制台 EOF;Windows 不符合这一点。我的观点是——他可以“尝试”你的建议,但它会失败——所以这显然不是一个答案。这不是不正确的,只是不是答案。
2赞 dgnuff 8/12/2018 #2

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

所以改变这一行:

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

对此:

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

然后添加:

#define CTRL_Z ('Z' & 0x1f)

在程序的顶部。

您可能仍然需要在 - 之后键入 a 来获取程序要读取的缓冲输入,但它应该丢弃 ^Z 之后的所有内容。returnCtrlz

评论

0赞 alk 8/12/2018
你测试过这个吗?
0赞 Clifford 8/12/2018
我宁愿不被引用 - 至少不参考可能被删除的临时评论 - 它可能只会在以后造成混淆。欢迎您在我的评论中使用或重复信息,而无需确认。
0赞 dgnuff 8/12/2018
@afk是的,但我在转录时打错了。:/
0赞 dgnuff 8/12/2018
@Clifford 已编辑以尊重您的评论。郑重声明,我非常相信应得的信用。;)
1赞 alk 8/12/2018
请原谅,我完全错了。让我们清理一下。
1赞 Clifford 8/12/2018 #3

以下解决方案修复了 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 不同。当且仅当控制台输入缓冲区为空时,控制台会生成文件末尾,导致返回 EOF (-1),否则它会将 ASCII SUB (26) 字符插入到流中。SUB的使用最初是为了MS-DOS与更早的CP / M操作系统的兼容性。特别是,CP/M 文件由固定长度的记录组成,因此记录中间的 ^Z 用于指示不是记录长度精确倍数的文件的有效数据的结尾。在控制台中,如果 SUB 不在输入缓冲区的开头,并且 SUB 之后的所有字符都被丢弃,则 SUB 是可读的,而不是生成 EOF。这都是从路上开始的混乱宿醉。getchar()

0赞 AnT stands with Russia 8/13/2018 #4

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

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

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

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

    例如,如果您输入

    Hello^Z World^Z123
    

    并按你的C程序将实际读取序列。不会出现EOF条件。EnterHello\x1A

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

    例如,如果您输入

    ^ZHello World
    

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

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

评论

0赞 abhinavk 8/13/2018
感谢您的解释。泰。