C中标准输入的问题

时间:2015-03-06 20:46:38

标签: c input stdin getchar

我在C中创建一个读取输入的简单程序。然后显示使用的字符数。

我先尝试了什么:

#include <stdio.h>

int main(int argc, char** argv) {
    int currentChar;
    int charCount = 0;

    while((currentChar = getchar()) != EOF) {
        charCount++;
    }

    printf("Display char count? [y/n]");
    int response = getchar();

    if(response == 'y' || response == 'Y')
        printf("Count: %d\n",charCount);
}

发生了什么:

我会输入一些行并以^D结尾(我在Mac上)。该计划不会在int response = getchar();等待。我在网上发现这是因为输入流中仍然有内容。

我的第一个问题是内容会是什么?按^D输入EOF后我没有输入任何内容,当我尝试打印流中剩余的内容时,会打印?

我接下来尝试了什么:

假设输入流中还剩下字符,我做了一个清除输入缓冲区的函数:

void clearInputBuffer() {
    while(getchar() != '\n') {};
}

我在while循环之后调用了函数:

while((currentChar = getchar()) != EOF) {
    charCount++;
}
clearInputBuffer();

现在我假设在按下^D后仍有任何内容,它会被清除到下一个\n

但相反,我无法停止输入请求。当我按^D时,而不是将EOF发送到currentChar,终端上会显示^D

我知道网上可能有一个解决方案,但由于我不确定我的问题是什么,我真的不知道该找什么。

为什么会这样?有人也可以解释一下这个程序和终端幕后发生的事情吗?

3 个答案:

答案 0 :(得分:1)

Ctrl + D 在Unix上有点奇怪 - 它实际上并不是EOF字符。相反,它是向shell发出stdin应该关闭的信号。结果,行为可能有点不直观。连续两个 Ctrl + D ,或返回后跟 Ctrl + D < / kbd>,会为您提供您正在寻找的行为。我用这段代码测试了它:

#include <stdio.h>

int main(void) {
    size_t charcount = 0;

    while (getchar() != EOF)
        charcount++;

    printf("Characters: %zu\n", charcount);

    return 0;
}

已修改为包含chux格式字符建议。

答案 1 :(得分:1)

man 3 termios - 搜索VEOF。这将告诉你它实际上做了什么。

如果您需要更多解释,我首先要说ISO C stdin流有一个默认缓冲区,因此读取的任何字节都存储在该缓冲区中,除非以某种方式覆盖此行为(例如{{ 1}})。

setvbuf函数将从此默认缓冲区中读取,除非缓冲区中没有要读取的字符。在这种情况下,它将调用getchar函数将新数据实际存储到该缓冲区并返回读取的字节数。

但是,您的终端有自己的输入缓冲区。它将等待被识别为行尾(read)分隔符的输入序列。这是事情变得有趣的地方。如果EOL已启用,并且您已使用终端输入缓冲区中的字节 Ctrl + D ,那么您将有效地发送所有这些内容等待程序的字节,就像您输入了行尾分隔符一样。 ICANON函数将接收这些字节并将其存储在用于read的输入缓冲区中,从而导致stdin返回适当的值。

如果按下 Ctrl + D 且终端输入缓冲区中没有待处理字节,则不会发送任何数据,getchar将返回在read设置EOF流的文件结束指示符后,getchar会返回getchar

鉴于 Ctrl + D 的两种行为,按下两次将在第一次按键时发送所有未决字节,有效地清空终端&#39; s输入缓冲区,然后第二个键按下发送0个字节到stdin,这意味着read返回getchar并设置EOF的文件结束指示符。

如果发生错误(例如stdin已关闭),stdin本身将返回-1,read将在为{设置错误指示符后返回getchar {1}}流。以下内容可能有助于说明其工作原理,但TTY本身可能会在幕后进行,而不仅仅是等待EOFstdin并发送检测到任何一个后的数据:

getchar Flow Chart

当然,如果在控制终端上没有设置EOL,那么除非您的输入不是来自终端,否则您永远不会收到VEOF,因为突然出现某些特殊的键序列,如 Ctrl + D 由于该功能已关闭,因此无法识别为特殊键序列。

为了更加完整,请注意ICANON位和EOF内容通常不一定适用于Windows。 Windows命令提示符一次使用 Ctrl + Z ,并且Windows操作系统没有除ICANON C运行时函数之外的终端概念用于检测文件描述符是否指向涉及控制台句柄的文件描述。

按数据 Ctrl + Z 将有效取消其后的任何剩余输入,但行尾字符( Ctrl + M Enter )才能发送数据,除非使用termios Windows API函数禁用了已处理的输入。

如果在没有输入数据未决的情况下按下并通过输入行尾字符发送,则它将充当_isatty。例如,SetConsoleMode会导致EOF被读取,并且忽略包括hello^Z1234^M行尾字符在内的所有内容。 hello^Z或仅^M会触发^Z1234^M

操作系统很奇怪。

答案 2 :(得分:0)

你也可以这样做:

fseek(stdin,0,SEEK_END);

这对我来说很好。