Windows下C象棋引擎stdin读取仅支持256字符(预期8192)求助
在Windows 10系统中使用W64mingw开发遵循UCI协议的C语言象棋引擎时,遇到以下问题:程序在cmd.exe中运行时,无法输入或粘贴超过256字符的内容(Windows 10理论支持8192字符输入)。
例如,粘贴以下长字符串:
position startpos moves d2d4 g8f6 c2c4 e7e6 g1f3 b7b6 g2g3 c8a6 b2b3 f8b4 c1d2 b4e7 f1g2 c7c6 d2c3 d7d5 f3e5 f6d7 e5d7 b8d7 a2a4 d5c4 g2c6 a8c8 c6b5 a6b5 a4b5 d8c7 e1g1 d7f6 b3c4 c7c4 d1d2 f6e4 d2c1 e4c3 b1c3 c4d4 a1a7 e6e5 f1d1 d4c3 c1b1 e7c5 a7f7 e8f7 g3g4 c8f8 b1f5 f7g8 e2e4 f8f5 g4f5 c5d4 g1g2s c3c2 d1d4 e5d4 g2f3
会被截断为:
position startpos moves d2d4 g8f6 c2c4 e7e6 g1f3 b7b6 g2g3 c8a6 b2b3 f8b4 c1d2 b4e7 f1g2 c7c6 d2c3 d7d5 f3e5 f6d7 e5d7 b8d7 a2a4 d5c4 g2c6 a8c8 c6b5 a6b5 a4b5 d8c7 e1g1 d7f6 b3c4 c7c4 d1d2 f6e4 d2c1 e4c3 b1c3 c4d4 a1a7 e6e5 f1d1 d4c3 c1b1 e7c5 a7f7 e8f7
以下是复现问题的简化C程序(使用fgets从stdin读取数据):
#include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFFERSIZE 8192 void UCILoop() { static char lineIn[BUFFERSIZE]; setbuf(stdin, NULL); setbuf(stdout, NULL); while (1==1) { memset(&lineIn[0], 0, BUFFERSIZE); fflush(stdout); if (!fgets(lineIn, BUFFERSIZE, stdin)) continue; if (lineIn[0] == '\n') continue; printf("%s\n", lineIn); } } int main() { UCILoop(); }
相同环境下运行Stockfish等预编译象棋引擎无此问题,需解决该输入截断问题。
解决方案
1. 替换fgets为Windows API读取函数
问题根源在于W64mingw的标准库fgets在处理cmd.exe管道输入时,默认缓冲区限制可能被设为256字节。改用Windows原生API读取可绕过该限制:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #define BUFFERSIZE 8192 void UCILoop() { static char lineIn[BUFFERSIZE]; HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD bytesRead; setbuf(stdout, NULL); while (1) { memset(lineIn, 0, BUFFERSIZE); fflush(stdout); if (!ReadFile(hStdin, lineIn, BUFFERSIZE - 1, &bytesRead, NULL) || bytesRead == 0) continue; // 处理换行符并确保字符串以'\0'结尾 if (lineIn[bytesRead - 1] == '\n') { lineIn[bytesRead - 1] = '\0'; if (bytesRead > 1 && lineIn[bytesRead - 2] == '\r') lineIn[bytesRead - 2] = '\0'; } else { lineIn[bytesRead] = '\0'; } if (strlen(lineIn) == 0) continue; printf("%s\n", lineIn); } } int main() { UCILoop(); }
2. 修改标准库的缓冲区设置
若坚持使用标准库函数,可通过_setmode调整流模式来解决:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <io.h> #define BUFFERSIZE 8192 void UCILoop() { static char lineIn[BUFFERSIZE]; // 设置stdin为二进制模式,避免换行符转换干扰 _setmode(_fileno(stdin), _O_BINARY); setbuf(stdout, NULL); while (1==1) { memset(&lineIn[0], 0, BUFFERSIZE); fflush(stdout); if (!fgets(lineIn, BUFFERSIZE, stdin)) continue; // 转换CRLF为LF(按需处理) char* cr = strchr(lineIn, '\r'); if (cr) *cr = '\n'; if (lineIn[0] == '\n') continue; printf("%s\n", lineIn); } } int main() { UCILoop(); }
3. 编译时链接特定库
部分W64mingw版本中,标准库的控制台输入处理存在限制,编译时尝试链接msvcrt.lib替代默认库:
编译命令:
gcc your_program.c -o your_program.exe -lmsvcrt
内容的提问来源于stack exchange,提问作者Alberto

