Windows平台推荐用MultiByteToWideChar+WideCharToMultiByte双步转换UTF-8→GBK;Linux/macOS用iconv,注意编码名差异;禁用已废弃的std::codecvt系列。
在 C++ 中做 UTF-8 到 GBK 转换,没有标准库原生支持,必须依赖外部库或系统 API。Windows 下最直接可靠的是 MultiByteToWideChar + WideCharToMultiByte 组合;Linux/macOS 则需用 iconv 或 ICU。自己手写查表转换不可取,既不安全也不符合编码规范。
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 缓冲区长度WideCharToMultiByte(CP_ACP, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr) 获取目标 GBK 字节数std::vector 并执行第二步转换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());
}
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 才算完成E2BIG 失败errno 可能是 EILSEQ(非法序列)或 EINVAL(截断字符),需决定是跳过还是报错很多人试图用 std::codecvt_utf8 或 std::wst,但它们在 C++17 已被标记为 deprecated,且 GCC/Clang 实现不完整,
std::codecvt_byname("zh_CN.GBK") 在多数编译器上根本不可用。还有人把 GBK 当成固定双字节编码硬拆字节,结果遇到 0x81–0xFE 区间外的单字节 ASCII 或高位字节为 0xA1–0xA9 的全角标点就崩溃。
std::codecvt 系列——已废弃,行为不可控0xEF 0xBB 0xBF,需提前剥离再传给转换函数936(chcp 936),否则 cout 输出 GBK 字节会显示乱码真正麻烦的不是转换逻辑本身,而是错误处理粒度——你要在非法字节处停止、跳过、替换成问号,还是整个字符串拒绝转换?这得看业务场景,库不会替你决定。