Windows下UTF-8转GBK必须经UTF-16中转,即MultiByteToWideChar(CP_UTF8)→WideCharToMultiByte(936);Linux/macOS须用iconv,优先试"CP936"再fallback"GB18030";跨平台不可依赖STL codecvt或locale。

Windows下用MultiByteToWideChar + WideCharToMultiByte两步转换
UTF-8转GBK不是单次API能搞定的,Windows原生不支持UTF-8到GBK的直接映射。必须先转成UTF-16(即wchar_t序列),再从UTF-16转GBK。这是唯一稳定、无损、能处理全Unicode字符(包括中文、emoji、生僻字)的路径。
常见错误是跳过中间的wchar_t环节,试图用CP_UTF8直接转CP_GB2312或CP_ACP——这会失败或乱码,因为MultiByteToWideChar不接受CP_GB2312作为目标编码,WideCharToMultiByte也不接受CP_UTF8作为源编码来反向操作。
-
MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, nullptr, 0):先获取所需wchar_t缓冲区长度 -
MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, wstr, wlen):真正转换为wchar_t* -
WideCharToMultiByte(936, 0, wstr, -1, nullptr, 0, nullptr, nullptr):936是GBK的代码页,再次预估长度 -
WideCharToMultiByte(936, 0, wstr, -1, gbk_str, gbk_len, nullptr, nullptr):完成最终转换
注意:936是Windows对GBK的内部标识,不是字符串"GBK";-1表示输入以\0结尾,自动计算长度;若输入不含结束符,需传入明确字节数。
Linux/macOS下没有原生GBK支持,得靠iconv
POSIX系统不提供类似Windows的代码页API,必须用iconv库。它支持"UTF-8" → "GBK"直连,但要注意:不同glibc版本对"GBK"别名的支持程度不同,有些只认"CP936"或"GB18030"。
立即学习“C++免费学习笔记(深入)”;
典型错误是硬写"GBK"却在某发行版上返回iconv_open: Invalid argument。实测较稳妥的写法是优先尝试"CP936",失败后再 fallback 到"GB18030"(它是GBK超集,兼容所有GBK字符,且glibc普遍支持)。
- 调用
iconv_open("CP936", "UTF-8"),成功则用它 - 若返回
(iconv_t)-1,再试iconv_open("GB18030", "UTF-8") - 转换时务必检查
iconv()返回值:若为(size_t)-1且errno == E2BIG,说明输出缓冲区不够,需扩容重试 - 不要忽略
iconv_close(),否则有资源泄漏风险
跨平台封装时避免依赖系统locale
有人想用std::codecvt_utf8或std::locale配合std::use_facet,这条路已失效:std::codecvt_utf8只支持UTF-8 ↔ UTF-32/UTF-16,不支持GBK;C++20起std::codecvt系列已被弃用;且std::locale("")的行为依赖系统当前locale设置,比如Linux设为en_US.UTF-8时,根本无法触发GBK转换逻辑。
所以,跨平台项目里不要试图用STL做编码转换。要么条件编译:Windows走MultiByteToWideChar/WideCharToMultiByte,Linux/macOS走iconv;要么引入轻量第三方如utf8cpp(仅UTF处理)+ iconv兜底,但别指望一个头文件解决GBK。
- 不要用
setlocale(LC_ALL, "Chinese_China.936")这类Windows API模拟——它只影响printf等C函数,不影响编码转换逻辑 - 不要把
char*当GBK字符串直接传给std::string构造函数——std::string不关心编码,它只是字节容器,后续打印或写文件时才会出问题 - 若需运行时判断系统是否支持GBK,Linux可查
/usr/share/i18n/charmaps/GB18030.gz是否存在,但这属于过度工程,不如直接iconv_open试探
GBK不是UTF-8子集,转换失败要主动处理
UTF-8能表示所有Unicode字符,而GBK只覆盖约2万汉字+符号。遇到UTF-8里的生僻字(如U+30000以上)、emoji、数学符号,转GBK必然失败。Windows下WideCharToMultiByte默认静默替换为?,iconv默认中止并设errno = EILSEQ。
生产环境必须显式控制错误策略,不能依赖默认行为:
- Windows:调用
WideCharToMultiByte时第2个参数加WC_NO_BEST_FIT_CHARS,并传入非nullptr的lpDefaultChar和lpUsedDefaultChar,以便捕获替换事件 - Linux:
iconv前调用iconvctl(cd, ICONV_SET_DISCARD_ILLEGAL, &discard)(glibc 2.34+),或手动检查返回值+errno做降级(比如记录原始UTF-8 hex并填???) - 永远不要假设“用户输入的中文肯定能转GBK”——网页表单、手机键盘、OCR结果都可能含超出GBK范围的字符
最易被忽略的是:转换后没验证输出长度或内容有效性。比如空字符串、全?、截断的半字节,这些都该在业务逻辑前做基础校验。


















