Windows下如何以非阻塞方式检测stdin缓冲区是否为空?
Windows 平台实现方案
你可以通过 Windows 原生API PeekNamedPipe 实现非阻塞检测 stdin 缓冲区是否有未读字符,逻辑和 Linux 端的非阻塞 read 完全对齐,实现步骤如下:
- 首先需要修改控制台默认模式,关闭行缓冲、回显,否则无法读取单个按键输入,建议程序启动时保存原控制台模式,退出时恢复,避免控制台行为异常。
- 在
_WIN32分支下通过PeekNamedPipe先查询缓冲区待读字节数,大于0时再执行读操作,无数据直接返回-1。
完整可运行的跨平台代码如下:
#include <stdio.h> #if defined _WIN32 #include <windows.h> // 保存原控制台模式,程序退出时恢复 static DWORD orig_console_mode; static BOOL console_initialized = FALSE; // 初始化控制台模式,程序启动时调用一次即可 void init_console() { HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); GetConsoleMode(hStdin, &orig_console_mode); // 关闭行输入、回显、控制键处理,可根据需求调整参数 SetConsoleMode(hStdin, orig_console_mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)); console_initialized = TRUE; } // 恢复控制台模式,程序退出前必须调用 void restore_console() { if (console_initialized) { HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); SetConsoleMode(hStdin, orig_console_mode); } } #else #include <unistd.h> #include <termios.h> // Linux端控制台初始化可选,如果你已经自行关闭了行输入模式可以省略 #endif int check_keydown() { #if defined _WIN32 if (!console_initialized) return -1; HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD avail_bytes = 0; // 非阻塞查询缓冲区可用字节数,不实际读取内容 if (!PeekNamedPipe(hStdin, NULL, 0, NULL, &avail_bytes, NULL)) { return -1; } if (avail_bytes > 0) { char ch; DWORD read_bytes; if (ReadFile(hStdin, &ch, 1, &read_bytes, NULL) && read_bytes == 1) { return (unsigned char)ch; } } return -1; #else int ch = 0; int res = read(STDIN_FILENO, &ch, 1); if (res > 0) return ch; return -1; #endif }
注意事项
- 上述实现默认针对控制台输入场景,如果需要支持stdin被重定向到文件/管道的场景,可以先通过
GetFileType(hStdin)判断输入类型,非控制台输入可以直接使用CRT封装的_kbhit()+_getch()组合实现,底层逻辑和上述代码完全一致。 - 必须在程序退出前调用
restore_console()恢复控制台默认模式,否则会出现控制台输入不显示、无法正常换行等异常问题。
内容的提问来源于stack exchange,提问作者user16823964
相关产品推荐
相关产品推荐

