You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将管道读取函数从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文本,想知道修改时哪里出错了?

错误原因分析

你的修改有两个核心问题:

  1. 字节数与宽字符数混淆:

    • dwAvail是管道中可用的字节数,buf.size() * sizeof(wchar_t)是缓冲区能容纳的总字节数(每个wchar_t占2字节)。但读取后dwRead记录的是实际读取的字节数,不是宽字符数。你直接把整个buf拼接到结果里,未被填充的'\0'和不完整的宽字符会导致乱码。
    • 正确的做法应该是用dwRead / sizeof(wchar_t)计算实际读取的宽字符数,截断buf后再拼接。
  2. 编码不匹配:

    • 管道返回的是ASCII单字节文本,你直接用std::wstring读取,相当于把单字节数据当成双字节的UTF-16编码处理。如果读取的字节数是奇数,最后一个字节会和后续的'\0'组成无效宽字符,自然出现乱码。

正确的修改方案

针对你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 14:14:35