getchar 行为
对于 linux,EOF 字符是用 ctrl + d 编写的,而在 Windows 上,它是由控制台在您按下 enter 后编写的通过 ctrl + z 更改 CRT 库的内部状态(保留此行为是为了与非常旧的系统兼容)。如果我没记错的话,它被称为文件的软结束。我不认为你可以绕过它,因为当你按下 enter 而不是按下 ctrl + 时,你的 getchar 实际上会消耗 EOF 字符z.
据报道here:
在 Microsoft 的 DOS 和 Windows(以及 CP/M 和许多 DEC 操作系统)中,从终端读取永远不会产生 EOF。相反,程序识别源是终端(或其他“字符设备”)并将给定的保留字符或序列解释为文件结束指示符;最常见的是 ASCII Control-Z,代码 26。一些 MS-DOS 程序,包括 Microsoft MS-DOS shell (COMMAND.COM) 的一部分和操作系统实用程序(如 EDLIN),将文本文件中的 Control-Z 标记有意义数据的结尾,和/或在写入文本文件时将 Control-Z 附加到末尾。这样做有两个原因:
其他信息也上报here:
一些现代文本文件格式(例如 CSV-1203[6])仍然建议将尾随 EOF 字符作为文件中的最后一个字符附加。但是,键入 Control+Z 不会将 EOF 字符嵌入到 MS-DOS 或 Microsoft Windows 中的文件中,这些系统的 API 也不会使用该字符来表示文件的实际结尾。
在使用内置文本文件读取原语(INPUT、LINE INPUT 等)时,某些编程语言(例如 Visual Basic)不会读取超过“软”EOF,因此必须采用替代方法,例如以二进制模式打开文件或使用文件系统对象超越它。
字符 26 用于标记“文件结束”,即使 ASCII 将其称为替代,并且有其他字符。
如果你这样修改你的代码:
#include <stdio.h>
int main() {
while(1) {
char c = getchar();
printf("%d\n", c);
if (c == EOF) // tried with also -1 and 26
break;
}
return 0;
}
然后您对其进行测试,在 Windows 上您会看到 EOF (-1) 在您按下 enter 之前它不会写入控制台。除此之外,^Z 是由终端仿真器打印的(我怀疑)。根据我的测试,如果出现以下情况,则会重复此行为:
- 您使用 Microsoft 编译器进行编译
- 你使用 GCC 编译
- 在 CMD 窗口中运行编译好的代码
- 你在windows的bash模拟器中运行编译后的代码
使用 Windows 控制台 API 更新
按照@eryksun 的建议,我成功地为 Windows 编写了一个(它可以做什么复杂得可笑)代码,它改变了 conhost 的行为,以实际获得“按 ctrl + d”。它不能处理所有事情,这只是一个例子。 恕我直言,这是要尽可能避免的事情,因为可移植性小于 0。此外,要真正正确处理其他输入情况,应该编写 更多 代码,因为这些东西将标准输入从控制台中分离出来,你必须自己处理。
这些方法或多或少的工作如下:
- 获取标准输入的当前处理程序
- 创建一个输入记录数组,该结构包含有关在主机窗口中发生的信息(键盘、鼠标、调整大小等)的信息
- 读取窗口中发生的事情(它可以处理事件的数量)
- 遍历事件向量以处理键盘事件并截取退出或打印任何其他 ascii 字符所需的 EOF(即我测试的 4)。
这是代码:
#include <windows.h>
#include <stdio.h>
#define Kev input_buffer[i].Event.KeyEvent // a shortcut
int main(void) {
HANDLE h_std_in; // Handler for the stdin
DWORD read_count, // number of events intercepted by ReadConsoleInput
i; // iterator
INPUT_RECORD input_buffer[128]; // Vector of events
h_std_in = GetStdHandle( // Get the stdin handler
STD_INPUT_HANDLE // enumerator for stdin. Others exist for stdout and stderr
);
while(1) {
ReadConsoleInput( // Read the input from the handler
h_std_in, // our handler
input_buffer, // the vector in which events will be saved
128, // the dimension of the vector
&read_count); // the number of events captured and saved (always < 128 in this case)
for (i = 0; i < read_count; i++) { // and here we iterate from 0 to read_count
switch(input_buffer[i].EventType) { // let's check the type of event
case KEY_EVENT: // to intercept the keyboard ones
if (Kev.bKeyDown) { // and refine only on key pressed (avoid a second event for key released)
// Intercepts CTRL + D
if (Kev.uChar.AsciiChar != 4)
printf("%c", Kev.uChar.AsciiChar);
else
return 0;
}
break;
default:
break;
}
}
}
return 0;
}