Windows下GBK转UTF-8必须分两步:先用MultiByteToWideChar(CP_ACP,…)将GBK转UTF-16,再用WideCharToMultiByte(CP_UTF8,…)转UTF-8;需用"wb"二进制模式写文件,避免BOM和换行符干扰。

Windows 下用 MultiByteToWideChar + WideCharToMultiByte 转 GBK 到 UTF-8
Windows API 没有直接从 GBK 到 UTF-8 的单步函数,必须走「GBK → UTF-16(宽字符)→ UTF-8」两步。这是最稳妥、兼容性最好的做法,绕不开。
关键点在于:第一步用 CP_ACP 表示当前系统 ANSI 代码页(中文 Windows 默认是 GBK),第二步目标编码必须指定 CP_UTF8。
- 别用
CP_GBK—— 这个宏并不存在,Windows 不认 - 第一步的
cbMultiByte参数要传 -1 才能自动包含末尾\0,否则转换后的宽字符串可能缺结尾 - 第二步输出缓冲区大小必须足够:UTF-8 中一个汉字最多占 3 字节,而 UTF-16 是固定 2 字节,所以目标缓冲区长度建议设为
wlen * 3 + 1
写入文件前必须用 fopen 打开二进制模式("wb")
如果用文本模式("w")写 UTF-8,VC 运行时会在 Windows 下偷偷把 \n 换成 \r\n,更糟的是——某些旧版 CRT 会把连续的 0xEF 0xBB 0xBF(BOM)误判为“带签名 UTF-8”,触发内部编码转换逻辑,导致乱码。
正确姿势就是彻底绕过 C 标准库的文本处理:
立即学习“C++免费学习笔记(深入)”;
- 一律用
fopen(..., "wb"),写完后手动加换行符(如果需要) - 或者改用
CreateFileW+WriteFile,完全走 Win32 API,更可控 - 千万别在 UTF-8 文件开头写 BOM —— Windows 记事本喜欢它,但绝大多数现代工具(VS Code、git、编译器)视其为脏字节,可能报错或跳过解析
WideCharToMultiByte 返回 0 时,用 GetLastError() 查具体原因
常见失败不是因为编码错,而是缓冲区太小或参数非法。比如:
- 传了
NULL给lpMultiByteStr却没把cchWideChar设对,返回值是所需字节数,但容易误当成成功 -
dwFlags传了WC_ERR_INVALID_CHARS,但源字符串里真有非法 GBK 字节(比如单独的0x80),就会直接失败 - 目标缓冲区长度设成
0或负数,GetLastError()返回ERROR_INVALID_PARAMETER,不是编码问题
调试时先调一次获取长度,再 malloc 分配,再调第二次写入 —— 这是标准安全流程,省不了。
完整可运行片段(无异常处理,仅示意主干逻辑)
const char* gbk_str = "\xC4\xE3\xBA\xC3"; // "你好" 的 GBK 编码
int len = (int)strlen(gbk_str);
<p>// Step 1: GBK -> UTF-16
int wlen = MultiByteToWideChar(CP_ACP, 0, gbk_str, len, NULL, 0);
wchar_t* wstr = new wchar_t[wlen + 1];
MultiByteToWideChar(CP_ACP, 0, gbk_str, len, wstr, wlen);</p><p>// Step 2: UTF-16 -> UTF-8
int utf8_len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, NULL, 0, NULL, NULL);
char* utf8_str = new char[utf8_len + 1];
WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, utf8_str, utf8_len, NULL, NULL);</p><p>// Write binary
FILE* fp = fopen("out.txt", "wb");
fwrite(utf8_str, 1, utf8_len, fp);
fclose(fp);</p><p>delete[] wstr;
delete[] utf8_str;</p>注意:MultiByteToWideChar 和 WideCharToMultiByte 都不保证目标字符串以 \0 结尾,除非你显式传入长度含末位,或自己补零。写文件时只写有效字节数(utf8_len),别多写。
真正麻烦的从来不是调两个 API,而是确保每一步的长度计算和内存分配都严格匹配 —— 少一个字节,就可能截断半个汉字;多一个字节,又可能把栈上随机值写进文件。



















