C语言中标准输入的问题

C语言中标准输入的问题,c,input,stdin,getchar,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 cou

我正在用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);
}
我在while循环之后调用了函数:

while((currentChar = getchar()) != EOF) {
    charCount++;
}
clearInputBuffer();
现在,我假设在按下
^D
之后,如果还有剩余的内容,它将被清除到下一个
\n

但是,我不能停止输入请求。当我按下
^D
,而不是将
EOF
发送到
currentChar
,终端上将显示
^D

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

为什么会这样?有人能解释一下这个程序和终端的幕后到底发生了什么吗?

Ctrl+D在Unix上有点奇怪——它实际上不是EOF字符。相反,它是给shell的一个信号,
stdin
应该关闭。因此,该行为可能有些不直观。一行中有两个Ctrl+D,或者一个回车符后跟一个Ctrl+D,这将为您提供所需的行为。我使用以下代码对其进行了测试:

#include <stdio.h>

int main(void) {
    size_t charcount = 0;

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

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

    return 0;
}
#包括
内部主(空){
大小\u t字符数=0;
while(getchar()!=EOF)
charcount++;
printf(“字符数:%zu\n”,字符数);
返回0;
}
编辑以包含的格式字符建议。

-搜索
VEOF
。这将告诉你它的实际用途

如果您需要更多的解释,我将首先说明ISO C
stdin
流有一个默认缓冲区,因此任何读取的字节都存储到该缓冲区中,除非以某种方式重写此行为(例如
setvbuf

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

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

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

考虑到Ctrl+D的两种行为,按下两次将在第一次按键时发送所有待处理字节,有效地清空终端的输入缓冲区,然后第二次按键将0字节发送到
读取
,这意味着
getchar
返回
EOF
,并且设置了
stdin
的文件结束指示器

如果发生错误(例如,
stdin
已关闭),
read
本身将返回-1,
getchar
将在设置
stdin
流的错误指示器后返回
EOF
。以下内容可能有助于说明其工作原理,尽管TTY本身的幕后工作可能远不止等待
EOL
VEOF
并在检测到其中一个后发送数据:

当然,如果控制终端上未设置
ICANON
,则您将永远不会收到
EOF
,除非您的输入不是来自终端,因为由于功能关闭,某些特殊按键序列(如Ctrl+D)突然无法识别为特殊按键序列

为了更完整一点,请注意,
ICANON
bit和
termios
内容通常不一定在Windows上应用很多。Windows命令提示符首先使用Ctrl+Z,Windows操作系统除了用于检测文件描述符是否指向涉及控制台句柄的文件描述的
\u isatty
C运行时函数之外,没有任何终端概念

在数据挂起的情况下按Ctrl+Z将有效地取消其后的任何剩余输入,尽管仍需要按行尾字符(Ctrl+M或Enter)才能发送数据,除非使用
SetConsoleMode
Windows API函数禁用了已处理的输入

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

操作系统很奇怪。

你也可以这样做:

fseek(stdin,0,SEEK_END);

这对我来说很好。

size\u t
%u
建议
printf(“字符:%zu\n”,字符数)不一定正确
printf(“字符:%llu\n”,(无符号长字符)字符数)
在标准输入上读取一次EOF后,除非使用
clearerr(stdin),否则将继续获取EOF
将其重置为尝试读取字符的状态。您可以尝试通过在代码中打印
response
的值来调试此问题(使用
%d
,因为
response
是一个
int
),当您在输出中看到
-1
时,您可能有机会猜测发生了什么。最重要的方法之一是
fseek(stdin,0,SEEK_END);