Windows平台推荐用MultiByteToWideChar+WideCharToMultiByte双步转换UTF-8→GBK;Linux/macOS用iconv,注意编码名差异;禁用已废弃的std::codecvt系列。

在 C++ 中做 UTF-8 到 GBK 转换,没有标准库原生支持,必须依赖外部库或系统 API。Windows 下最直接可靠的是 MultiByteToWideChar + WideCharToMultiByte 组合;Linux/macOS 则需用 iconv 或 ICU。自己手写查表转换不可取,既不安全也不符合编码规范。
Windows 平台用 WinAPI 双步转换(推荐)
UTF-8 → wchar_t(UTF-16)→ GBK 是 Windows 最稳妥的路径,避免中间编码歧义。注意:CP_UTF8 和 CP_ACP(即系统默认 ANSI 代码页,通常为 GBK)必须明确指定,不能省略。
-
MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, nullptr, 0)先获取所需wchar_t缓冲区长度 - 分配足够空间后再次调用,完成 UTF-8 → UTF-16 转换
-
WideCharToMultiByte(CP_ACP, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr)获取目标 GBK 字节数 - 再分配
std::vector并执行第二步转换 - 若输入含非法 UTF-8 序列,第一个 API 返回 0,需检查
GetLastError()==ERROR_NO_UNICODE_TRANSLATION
std::string utf8_to_gbk(const std::string& utf8_str) {
if (utf8_str.empty()) return {};
int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, nullptr, 0);
if (wlen == 0) return {};
std::vector wstr(wlen);
MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, wstr.data(), wlen);
int len = WideCharToMultiByte(CP_ACP, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr);
if (len == 0) return {};
std::vector gbk(len);
WideCharToMultiByte(CP_ACP, 0, wstr.data(), -1, gbk.data(), len, nullptr, nullptr);
return std::string(gbk.data());
}
Linux/macOS 用 iconv(跨平台首选)
iconv 是 POSIX 标准接口,glibc 和 macOS 都内置支持。关键点在于:源编码名必须是 "UTF-8"(不能带横线或空格),目标编码名在 Linux 上常用 "GBK",macOS 则需用 "CP936"(二者等价,但 macOS 的 iconv -l 不识别 GBK)。
- 调用
iconv_open("GBK", "UTF-8")后必须检查返回值是否为(iconv_t)-1 -
iconv()是流式转换,需循环处理,*inbytesleft为 0 才算完成 - 输出缓冲区要预留足够空间(一般按输入字节数 × 2 估算),否则会因
E2BIG失败 - 转换失败时
errno可能是EILSEQ(非法序列)或EINVAL(截断字符),需决定是跳过还是报错
别踩这些坑
很多人试图用 std::codecvt_utf8 或 std::wstring_convert,但它们在 C++17 已被标记为 deprecated,且 GCC/Clang 实现不完整,std::codecvt_byname("zh_CN.GBK") 在多数编译器上根本不可用。还有人把 GBK 当成固定双字节编码硬拆字节,结果遇到 0x81–0xFE 区间外的单字节 ASCII 或高位字节为 0xA1–0xA9 的全角标点就崩溃。
立即学习“C++免费学习笔记(深入)”;
- 不要用
std::codecvt系列——已废弃,行为不可控 - 不要假设 GBK 字符一定是两个字节——ASCII 部分仍是单字节
- 不要忽略 BOM:UTF-8 文件可能带
0xEF 0xBB 0xBF,需提前剥离再传给转换函数 - Windows 下测试时,确保控制台代码页是
936(chcp 936),否则cout输出 GBK 字节会显示乱码
真正麻烦的不是转换逻辑本身,而是错误处理粒度——你要在非法字节处停止、跳过、替换成问号,还是整个字符串拒绝转换?这得看业务场景,库不会替你决定。











