Windows下必须用MultiByteToWideChar+WideCharToMultiByte经wchar_t中转,因API不支持GBK↔UTF-8直接映射;Linux/macOS须用iconv,编码名用"GBK"而非"CP936";跨平台需条件编译,std::codecvt已废弃不可用。

Windows 下必须用 MultiByteToWideChar + WideCharToMultiByte 经 wchar_t 中转;Linux/macOS 必须用 iconv,且编码名要用 "GBK" 而非 "CP936";跨平台项目得条件编译,不能硬写统一逻辑。
Windows 平台:为什么非得走 UTF-16 中转?
因为 Windows 的多字节转换 API 不支持 GBK ↔ UTF-8 直接映射。它只认代码页 ID(如 CP_ACP、CP_UTF8),不接受字符串形式的编码名(比如传 "GBK" 会直接失败)。中间用 wchar_t 是强制路径,不是优化选择。
-
MultiByteToWideChar(CP_ACP, ...)在简体中文系统上等价于CP_936,比硬写936更可读、更健壮 - 两次调用模式是标准做法:第一次传
nullptr查长度,第二次分配缓冲区再转——否则容易截断或越界 - 输入含
\0字节时,绝不能用c_str(),必须用data()和size()显式传长度,否则从第一个\0就停了 - 返回值为 0 且
GetLastError()==123(ERROR_INVALID_NAME),大概率是源编码参数写反了,比如把CP_UTF8误当成了源编码传给 GBK 转换
Linux/macOS 平台:iconv 编码名到底该填什么?
iconv_open("UTF-8", "GBK") 是 Linux 上最稳写法;macOS 上部分版本也认,但若失败可试 "CP936";"GB2312" 仅覆盖子集,慎用。传错编码名会导致 iconv_open 返回 (iconv_t)-1 且 errno == EINVAL,这不是数据问题,是名字不被识别。
-
iconv的inbuf和outbuf是双指针(char**),函数内部会移动它们位置,别反复传同一个栈变量地址 - 输出缓冲区建议按输入长度 × 3 预留(UTF-8 单字符最多占 3 字节),
iconv不自动补\0,转完要手动加 - 遇到非法字节序列时,
iconv默认静默跳过,不会报错——这容易掩盖原始数据损坏,必要时应先校验输入是否合法 GBK - glibc 2.34+ 对
"GBK"支持已稳定,但旧版嵌入式系统仍可能只认"CP936",需实测
std::string 处理含 \0 的 GBK 字符串时踩什么坑?
GBK 编码里 <p>GBK 编码里 <code>\0 是合法字节(例如 \xA1\xA1 后跟 \0 可能是有效双字节字符的一部分),而 std::string(const char*) 构造函数和 strlen() 都会把它当字符串结尾,导致提前截断。
\xA1\xA1 后跟 <p>GBK 编码里 <code>\0 是合法字节(例如 \xA1\xA1 后跟 \0 可能是有效双字节字符的一部分),而 std::string(const char*) 构造函数和 strlen() 都会把它当字符串结尾,导致提前截断。 可能是有效双字节字符的一部分),而 std::string(const char*) 构造函数和 strlen() 都会把它当字符串结尾,导致提前截断。
立即学习“C++免费学习笔记(深入)”;
- 所有 API 调用必须传
str.data()和str.size(),而不是str.c_str() - 用
std::string存 GBK 原始字节流没问题,但别用它做“字符串操作”(比如find、substr),除非你确定没\0 - 调试时打印
str.data()得用printf("%.*s", (int)str.size(), str.data()),否则看不到后面内容 - 若需频繁操作,建议封装成
struct gbk_bytes { std::string data; },并在接口层强制检查长度来源
为什么 std::codecvt 完全不能用?
std::codecvt_utf8 根本不支持 GBK,C++ 标准库从未提供 codecvt_byname("GBK") 实现;VS 2015 起默认禁用,GCC/Clang 早已标记 deprecated,C++20 直接移除。试图靠它转码只会编译失败或运行时崩溃。
- 已有项目还在用,说明它早该被替换了——不是“不够好”,而是“根本不可用”
- Boost.Locale 虽可用,但依赖 Boost 构建体系,对轻量级项目负担大;ICU 更重,一般只在需要完整 Unicode 处理时引入
- 真正跨平台又不想绑死第三方库的方案,就是封装一层条件编译:Windows 走 WinAPI,POSIX 系统走
iconv,连头文件都不同,没法假装统一
最易忽略的点是:所有转换函数都假设输入字节流本身是合法编码。如果原始 GBK 数据就含非法序列(比如单个 \xA1),Windows API 会返回失败,iconv 则默认跳过——没人替你修复烂数据,得在转换前加校验或约定容错策略。


















