getchar的行为
对于Linux系统,EOF字符可以通过ctrl+d来输入,而在Windows系统中,当你通过ctrl+z改变CRT库的内部状态后,通过按下enter键来写入该字符(这种行为是为了与非常老的系统保持兼容性)。如果我没记错的话,它被称为软文件结尾。我认为你无法绕过它,因为当你按下enter键时,EOF字符实际上已被你的getchar
函数消耗掉,而不是在你按下ctrl+z时。
正如这里所述:
在Microsoft的DOS和Windows中(以及在CP/M和许多DEC操作系统中),从终端读取永远不会产生EOF。相反,程序会识别源为终端(或其他“字符设备”),并将给定的保留字符或序列解释为文件结束指示符;最常见的是ASCII控制字符-Z,代码26。一些MS-DOS程序,包括微软MS-DOS shell(COMMAND.COM)的部分和操作系统实用程序(如EDLIN),将文本文件中的Control-Z视为标记有意义数据的结尾,并/或在写入文本文件时在末尾附加Control-Z。这是出于两个原因:
其他信息也可以在这里找到:
一些现代文本文件格式(例如CSV-1203)仍建议在文件末尾附加一个尾随EOF字符。然而,在MS-DOS或Microsoft Windows中,键入Control+Z不会将EOF字符嵌入文件中,系统的API也不使用该字符来表示实际的文件结尾。
一些编程语言(例如Visual Basic)在使用内置的文本文件读取基元(INPUT、LINE INPUT等)时,不会读取“软”EOF之后的内容,必须采用其他方法,例如以二进制模式打开文件或使用文件系统对象来超越它。
字符26被用于标记“文件结尾”,即使ASCII将其称为替代符号,并且还有其他字符可以用于此目的。
如果您将您的代码修改为以下形式:
#include <stdio.h>
int main() {
while(1) {
char c = getchar();
printf("%d\n", c);
if (c == EOF)
break;
}
return 0;
}
如果你在Windows上测试它,你会发现只有在按下
enter键之前,控制台才不会显示
EOF
(-1)。在此之前,终端仿真器会打印一个
^Z
(我猜测)。从我的测试来看,如果:
- 使用Microsoft编译器编译
- 使用GCC编译
- 在CMD窗口中运行已编译的代码
- 在Windows中运行bash仿真器中的已编译代码
使用Windows控制台API进行更新
根据@eryksun的建议,我成功地编写了一段(为它可以做的事情而言过于复杂)的Windows代码,改变了conhost的行为,以实际获得“按ctrl+d退出”的功能。它并不能处理所有情况,它只是一个例子。在我看来,这是要尽可能避免的事情,因为它的可移植性小于0。此外,为了正确处理其他输入情况,需要编写更多的代码,因为这个东西将stdin从控制台分离出来,你必须自己处理。
该方法的工作原理如下:
- 获取标准输入的当前处理程序
- 创建一个输入记录数组,该结构包含有关conhost窗口中发生的事件的信息(键盘、鼠标、调整大小等)
- 读取窗口中发生的事件(它可以处理事件数量)
- 遍历事件向量以处理键盘事件并截获所需的EOF(从我测试的结果来看,它是4),或打印任何其他ascii字符。
这是代码:
#include <windows.h>
#include <stdio.h>
#define Kev input_buffer[i].Event.KeyEvent
int main(void) {
HANDLE h_std_in;
DWORD read_count,
i;
INPUT_RECORD input_buffer[128];
h_std_in = GetStdHandle(
STD_INPUT_HANDLE
);
while(1) {
ReadConsoleInput(
h_std_in,
input_buffer,
128,
&read_count);
for (i = 0; i < read_count; i++) {
switch(input_buffer[i].EventType) {
case KEY_EVENT:
if (Kev.bKeyDown) {
if (Kev.uChar.AsciiChar != 4)
printf("%c", Kev.uChar.AsciiChar);
else
return 0;
}
break;
default:
break;
}
}
}
return 0;
}
getchar()
- 你只需要一次,并将其返回的值赋给一个变量,以便在循环内使用。 - Chris Turner