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

如何使用ReadFile读取多字节编码字符并实现正确控制台输出?

ReadFile读取多字节字符的问题排查与解决

问题场景

尝试用ReadFile读取文件内容到wchar_t数组并输出到控制台,代码如下:

DWORD read_output()
{
    BOOL success = FALSE;
    DWORD dwRead;
    HANDLE handle = CreateFileW(L"data.txt",
                                GENERIC_READ,
                                0,
                                NULL,
                                3,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    if (handle == INVALID_HANDLE_VALUE)
        printf("Failed to open file\n");
    do
    {
        wchar_t buffer[128];
        success = ReadFile(handle, buffer, 128, &dwRead, NULL);
        wprintf(L"%s", buffer);
    } while(!success || dwRead == 0);
    return 0;
}


int main()
{
    _setmode(fileno(stdout), _O_U16TEXT);
    read_output();
}

实际输出乱码:

ШиÑ
      Ñование.txt  اÙ

期望输出:

Шифрование.txt  العربية.txt

移除L"%s"后输出变为:

퀠킨톸톄킀킾킲킰킽킸⺵硴⁴�������⺩硴ੴ

错误原因拆解

  1. ReadFile字节数参数错误
    ReadFile的第三个参数是要读取的字节数,而wchar_t是2字节宽的类型。代码里传128,实际只读取了64个wchar_t,剩下的缓冲区内容未初始化,导致乱码。

  2. 文件编码与读取逻辑不匹配
    目标文件data.txt是UTF-8编码(从乱码特征判断:西里尔文和阿拉伯文的UTF-8字节被直接当作UTF-16解析),但代码直接把读取到的字节塞进wchar_t数组(UTF-16),没有做编码转换,必然乱码。

  3. 循环条件逻辑错误
    原代码的do-while条件是!success || dwRead == 0,这意味着读取失败或者读到0字节时还会继续循环,完全搞反了逻辑——正确的循环条件应该是读取成功且读到了字节(success && dwRead > 0)。

  4. 缺少字符串终止符
    ReadFile不会自动给读取的内容加\0终止符,wprintf(L"%s")需要以\0结尾的字符串,否则会输出缓冲区里的垃圾数据。

修正方案

根据文件编码分两种情况处理:

情况1:文件是UTF-8编码(最常见)

先读取UTF-8字节流,再转换为UTF-16(wchar_t)后输出:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

DWORD read_output()
{
    BOOL success = FALSE;
    DWORD dwRead;
    HANDLE handle = CreateFileW(L"data.txt",
                                GENERIC_READ,
                                FILE_SHARE_READ,
                                NULL,
                                OPEN_EXISTING, // 用宏代替魔数3
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    if (handle == INVALID_HANDLE_VALUE)
    {
        printf("Failed to open file, error: %lu\n", GetLastError());
        return GetLastError();
    }

    // 先读取UTF-8字节
    char utf8_buffer[256];
    do
    {
        success = ReadFile(handle, utf8_buffer, sizeof(utf8_buffer) - 1, &dwRead, NULL);
        if (!success || dwRead == 0)
            break;

        // 给UTF-8字符串加终止符
        utf8_buffer[dwRead] = '\0';

        // 转换UTF-8到UTF-16
        int wchar_count = MultiByteToWideChar(CP_UTF8, 0, utf8_buffer, -1, NULL, 0);
        wchar_t* utf16_buffer = malloc(wchar_count * sizeof(wchar_t));
        if (utf16_buffer)
        {
            MultiByteToWideChar(CP_UTF8, 0, utf8_buffer, -1, utf16_buffer, wchar_count);
            wprintf(L"%s", utf16_buffer);
            free(utf16_buffer);
        }
    } while (success && dwRead == sizeof(utf8_buffer) - 1); // 缓冲区满则继续读

    CloseHandle(handle);
    return 0;
}

int main()
{
    // 设置控制台为UTF-16输出模式
    _setmode(fileno(stdout), _O_U16TEXT);
    read_output();
    return 0;
}

情况2:文件本身是UTF-16编码(带BOM)

如果文件是UTF-16LE(Windows常用宽字节编码),可以直接读取到wchar_t数组,但要注意字节数参数和终止符:

#include <windows.h>
#include <stdio.h>

DWORD read_output()
{
    BOOL success = FALSE;
    DWORD dwRead;
    HANDLE handle = CreateFileW(L"data.txt",
                                GENERIC_READ,
                                FILE_SHARE_READ,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    if (handle == INVALID_HANDLE_VALUE)
    {
        printf("Failed to open file, error: %lu\n", GetLastError());
        return GetLastError();
    }

    wchar_t buffer[128];
    do
    {
        // 读取128个wchar_t,字节数是128*2
        success = ReadFile(handle, buffer, sizeof(buffer) - sizeof(wchar_t), &dwRead, NULL);
        if (!success || dwRead == 0)
            break;

        // 计算实际读取的wchar_t个数,加终止符
        int wchar_read = dwRead / sizeof(wchar_t);
        buffer[wchar_read] = L'\0';

        wprintf(L"%s", buffer);
    } while (success && dwRead == sizeof(buffer) - sizeof(wchar_t));

    CloseHandle(handle);
    return 0;
}

int main()
{
    _setmode(fileno(stdout), _O_U16TEXT);
    read_output();
    return 0;
}

ReadFile读取多字节字符的核心要点

  • 明确文件编码:多字节字符通常指UTF-8,而wchar_t是Windows的UTF-16实现,两者不能直接映射,必须用MultiByteToWideChar/WideCharToMultiByte做转换。
  • ReadFile参数注意:第三个参数是字节数,不是字符数。如果读取宽字符数组,要计算字节数(字符数 * sizeof(wchar_t)),且要留一个字符的位置放终止符。
  • 控制台输出适配:用_setmode(fileno(stdout), _O_U16TEXT)设置控制台为UTF-16输出模式,才能正确显示宽字符。
  • 字符串终止符:无论读取哪种编码,都要手动添加终止符,避免输出垃圾数据。
  • 循环逻辑正确:只有读取成功且读到数据时才继续循环,读完或失败就退出。

内容的提问来源于stack exchange,提问作者loaded_dypper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:01:04