提问人:Michael Hübler 提问时间:10/26/2017 最后编辑:Komal12Michael Hübler 更新时间:10/29/2017 访问量:3081
使用 EOF 结束循环(不带回车)
Ending a Loop with EOF (without enter)
问:
我目前正在尝试用这样的东西结束 while 循环:
#include <stdio.h>
int main()
{
while(getchar() != EOF)
{
if( getchar() == EOF )
break;
}
return 0;
}
当我按下我的 Ubuntu 时,它会立即结束循环。但是在 Windows 上,我必须按下然后按下才能关闭循环。我可以摆脱 Windows 上的吗?CTRL+D
CTRL+Z
ENTER
ENTER
答:
getchar 行为
对于 linux,EOF char 是用 + 编写的,而在 Windows 上,当您在通过 + 更改 CRT 库的内部状态后按下时,它是由控制台写入的(保留此行为是为了与非常旧的系统进行逆向兼容)。如果我没记错的话,它被称为文件的软端。我认为你不能绕过它,因为 EOF 字符实际上是在你按下时消耗的,而不是当你按 + 时。ctrldenterctrlzgetchar
enterctrlz
据以下报道:
在 Microsoft 的 DOS 和 Windows(以及 CP/M 和许多 DEC 操作系统)中,从终端读取永远不会产生 EOF。取而代之的是,程序识别源是终端(或其他“字符设备”),并将给定的保留字符或序列解释为文件结束指示符;最常见的是 ASCII Control-Z,代码 26。某些 MS-DOS 程序(包括Microsoft MS-DOS shell (COMMAND.COM) 和操作系统实用程序(如 EDLIN)的一部分)将文本文件中的 Control-Z 视为标记有意义数据的末尾,和/或在写入文本文件时将 Control-Z 追加到末尾。这样做有两个原因:
向后兼容 CP/M。CP/M 文件系统仅以 128 字节“记录”的倍数记录文件的长度,因此按照惯例,如果有意义数据在记录中间结束,则使用 Control-Z 字符来标记该数据的结尾。MS-DOS 文件系统始终记录文件的确切字节长度,因此在 MS-DOS 上从不需要这样做。
它允许程序使用相同的代码从终端和文本文件读取输入。
此处还报告了其他信息:
一些现代文本文件格式 (e.g. CSV-1203[6]) 仍然建议将尾随的 EOF 字符附加为文件中的最后一个字符。但是,键入 Ctrl+Z 不会将 EOF 字符嵌入到 MS-DOS 或 Microsoft Windows 中的文件中,这些系统的 API 也不会使用该字符来表示文件的实际结尾。
某些编程语言(例如 Visual Basic)在使用内置文本文件读取基元(INPUT、LINE INPUT 等)时不会读取“软”EOF,并且必须采用替代方法,例如以二进制模式打开文件或使用文件系统对象来超越它。
字符 26 用于标记“文件末尾”,即使 ASCII 将其称为 Substitute,并且还有其他字符。
如果像这样修改代码:
#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 上你会看到 (-1) 它没有写在控制台中,直到你按 .Beore of that a 是由终端仿真器打印的(我怀疑)。根据我的测试,如果出现以下情况,则会重复此行为:EOF
enter^Z
- 使用 Microsoft 编译器进行编译
- 使用 GCC 进行编译
- 在 CMD 窗口中运行编译的代码
- 在 Windows 的 Bash 模拟器中运行已编译的代码
使用 Windows 控制台 API 进行更新
按照@eryksun的建议,我成功地为Windows编写了一个(对于它可以做什么来说非常复杂)代码,该代码更改了conhost的行为,以实际获得“按+时退出”。它不能处理所有事情,它只是一个例子。恕我直言,这是尽可能避免的事情,因为可移植性小于 0。此外,为了正确处理其他输入情况,应该编写更多的代码,因为这些东西将 stdin 与控制台分离,您必须自己处理它。ctrld
这些方法或多或少地工作如下:
- 获取标准输入的当前处理程序
- 创建一个输入记录数组,该结构包含有关主机窗口中发生的情况(键盘、鼠标、调整大小等)的信息。
- 读取窗口中发生的情况(它可以处理事件数)
- 遍历事件向量以处理键盘事件并拦截退出所需的 EOF(即 4,来自我测试的内容)或打印任何其他 ascii 字符。
这是代码:
#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;
}
评论
ReadFile
ReadConsoleW
ReadConsoleW
pInputControl
GetConsoleMode
SetConsoleTitle
StandardInput
StandardOutput
StandardError
ConsoleHandle
ProcessParamaters
pInputControl
参数一起使用,而不是低级函数。具体来说,我建议使用位掩码,例如.它留下控制字符(例如 或 ),因此由您的代码来搜索它们并执行任何您想做的事情(例如,将其替换为 NUL)。ReadConsoleW
ReadConsoleInput
dwCtrlWakeupMask
inputCtrl.dwCtrlWakeupMask = 1 << ('D' - '@') | 1 << ('Z' - '@')
L"\x04"
L"\x1A"
while(getchar() != EOF)
{
if( getchar() == EOF )
break;
}
return 0;
这里是不一致的。
如果它将进入循环,否则(如果)它不会进入循环。因此,没有理由检查循环内部。getchar() != EOF
getchar() == EOF
getchar() == EOF
另一方面,您调用 2 次,您等待输入 2 个字符而不是仅输入 1 个字符。getchar()
你试着做了什么?
评论
ENTER
getchar()