将管道读取函数从char/CStringA改为std::wstring时遇乱码问题求助
问题描述
我尝试修改一段管道读取代码,将原代码里的CStringA改为std::wstring,同时把char buf[1024]替换成std::wstring buf,但修改后ReadFile执行后buf里全是乱码。
原函数代码:
char buf[1024]; DWORD dwRead = 0; DWORD dwAvail = 0; if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL)) break; if (!dwAvail) // No data available, return break; if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead) // Error, the child process might ended break; buf[dwRead] = 0; strResult += buf;
我的修改尝试:
std::wstring strResult; // ... std::wstring buf(1024, '\0'); DWORD dwRead = 0; DWORD dwAvail = 0; if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL)) break; if (!dwAvail) // No data available, return break; if (!::ReadFile(hPipeRead, buf.data(), min((buf.size() * sizeof(wchar_t)), dwAvail), &dwRead, NULL) || !dwRead) // Error, the child process might ended break; strResult += buf;
最终我改用了先读取std::string再转std::wstring的方案,能正常工作:
bool bProcessEnded = false; std::string result; for (; !bProcessEnded;) { // Give some timeslice (50 ms), so we won't waste 100% CPU. bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0; // Even if process exited - we continue reading, if // there is some data available over pipe. for (;;) { char buf[1024]; DWORD dwRead = 0; DWORD dwAvail = 0; if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL)) break; if (!dwAvail) // No data available, return break; if (!::ReadFile(hPipeRead, buf, min((sizeof(buf) - 1), dwAvail), &dwRead, NULL) || !dwRead) // Error, the child process might ended break; buf[dwRead] = 0; result += buf; } } //for CloseHandle(hPipeWrite); CloseHandle(hPipeRead); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return to_wstring(result);
我的场景中管道返回的是类似"already connected to 127.0.0.1:5555"的ASCII文本,想知道修改时哪里出错了?
错误原因分析
你的修改有两个核心问题:
字节数与宽字符数混淆:
dwAvail是管道中可用的字节数,buf.size() * sizeof(wchar_t)是缓冲区能容纳的总字节数(每个wchar_t占2字节)。但读取后dwRead记录的是实际读取的字节数,不是宽字符数。你直接把整个buf拼接到结果里,未被填充的'\0'和不完整的宽字符会导致乱码。- 正确的做法应该是用
dwRead / sizeof(wchar_t)计算实际读取的宽字符数,截断buf后再拼接。
编码不匹配:
- 管道返回的是ASCII单字节文本,你直接用
std::wstring读取,相当于把单字节数据当成双字节的UTF-16编码处理。如果读取的字节数是奇数,最后一个字节会和后续的'\0'组成无效宽字符,自然出现乱码。
- 管道返回的是ASCII单字节文本,你直接用
正确的修改方案
针对你的ASCII文本场景,有两种可靠的修改方式:
方式一:先读单字节再转宽字符(推荐)
保持原单字节缓冲区读取逻辑,将读取到的ASCII字符串转换为宽字符后拼接:
std::wstring strResult; // ... char buf[1024]; DWORD dwRead = 0; DWORD dwAvail = 0; if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL)) break; if (!dwAvail) break; if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead) break; buf[dwRead] = 0; // 将单字节字符串转换为宽字符串 strResult += std::wstring(buf, buf + dwRead);
方式二:直接用宽字符缓冲区(仅适用于UTF-16输出场景)
如果管道输出是UTF-16编码的宽字符数据,可以这样修改:
std::wstring strResult; // ... std::wstring buf(1024, L'\0'); DWORD dwRead = 0; DWORD dwAvail = 0; if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL)) break; if (!dwAvail) break; // 预留一个宽字符的空间放终止符,避免缓冲区溢出 DWORD maxBytesToRead = min(static_cast<DWORD>(buf.size() * sizeof(wchar_t) - sizeof(wchar_t)), dwAvail); if (!::ReadFile(hPipeRead, buf.data(), maxBytesToRead, &dwRead, NULL) || !dwRead) break; // 计算实际读取的宽字符数并截断缓冲区 DWORD wideCharsRead = dwRead / sizeof(wchar_t); buf.resize(wideCharsRead); strResult += buf;
内容的提问来源于Stack Exchange,提问作者Cesar
相关产品推荐
相关产品推荐

