Windows下最稳转换方式是MultiByteToWideChar(CP936)→WideCharToMultiByte(CP_UTF8),必须经wchar_t过渡;Linux/macOS用iconv("UTF-8","GBK"),禁用"CP936";std::string处理需用data()/size()避空字符截断;跨平台须条件编译区分CP936与"GBK"。

Windows下用MultiByteToWideChar + WideCharToMultiByte转换最稳
直接调系统API是Windows平台最可靠的方式,绕过第三方库依赖,也避免iconv在不同环境下的编码名歧义(比如"GBK"、"CP936"、"GB2312"混用出错)。
关键点在于:GBK → UTF-16(宽字符)→ UTF-8,两步不可省。中间必须用wchar_t过渡,因为Windows的MultiByteToWideChar只接受代码页ID(如CP936),不认"GBK"字符串。
-
MultiByteToWideChar(CP936, 0, gbk_str, -1, nullptr, 0)先查所需wchar_t缓冲区大小 - 分配足够空间后第二次调用,完成GBK → UTF-16转换
-
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr)同理查UTF-8长度 - 再分配、再转换,注意目标缓冲区要留出结尾
\0位置
Linux/macOS下iconv必须用"GBK"而非"CP936"
iconv在glibc和macOS上对GBK的支持名不统一:Linux认"GBK"或"GB2312",但"CP936"会失败;macOS则两者都可能支持,但实测"GBK"更通用。
常见错误是传"CP936"导致iconv_open返回(iconv_t)-1,且errno为EINVAL——这说明编码名不被识别,不是数据问题。
立即学习“C++免费学习笔记(深入)”;
- 调用前检查
iconv_open("UTF-8", "GBK")是否返回有效句柄 - 输入缓冲区指针和长度需用
const char**和size_t*传入iconv(),否则字节被跳过 - 输出缓冲区必须预留足够空间(UTF-8单字最多占3字节,但实际建议按输入长度×3分配)
- 转换后手动补
\0,iconv不负责字符串终结符
std::string转码时别忽略空字符和截断风险
GBK字符串里可能出现\0(比如“\0”本身是合法GBK码位),而std::string构造函数std::string(const char*)会提前截断。同样,strlen也会误判长度。
所有涉及长度计算的操作,必须用原始字节数(如data.size()),而不是strlen(data.c_str())。
- 输入
std::string gbk = "\xA1\xA1\0\xB2\xC2"(含中间\0),用c_str()传给API会只处理前两个字节 - 正确做法:传
gbk.data()和gbk.size(),确保整段二进制数据参与转换 - 输出结果也应构造为
std::string(utf8_ptr, utf8_len),而非std::string(utf8_ptr)
跨平台封装要注意CP936和"GBK"的条件编译
没有一套参数能在所有平台直接复用。Windows必须用数值代码页CP936,POSIX系统必须用字符串"GBK",硬写死一个会导致某平台编译通过但运行失败。
别试图用宏定义统一成TEXT("GBK")——Windows API不吃这个,iconv在Windows版(如MinGW)里又可能不认CP936字符串。
- 推荐按
#ifdef _WIN32分支:Windows走CP936路径,其他走"GBK"路径 - 若用CMake,可加
target_compile_definitions(mylib PRIVATE PLATFORM_WIN=${WIN32})辅助判断 - 测试时务必用含中文标点、生僻字(如“镕”、“堃”)的GBK字符串,避免仅测ASCII子集掩盖问题
真正麻烦的不是转换逻辑,而是确认输入字节流确实是合法GBK——损坏或混合编码的数据,任何转换函数都只能尽力而为,不会报错,但结果不可预期。


















