windows首选widechartomultibyte(cp_utf8,0,src,-1,nullptr,0,nullptr,nullptr)转utf-8,需两次调用;linux/macos禁用已弃用的std::codecvt_utf8_utf16,改用iconv或utf8cpp;跨平台应使用char16_t而非wchar_t并注意字节序。

Windows上用WideCharToMultiByte转换UTF-16到UTF-8最直接
Windows API 的 WideCharToMultiByte 是处理本地 UTF-16(即 wchar_t*)转 UTF-8 的首选,它不依赖第三方库,且能正确处理代理对(surrogate pairs)和 BOM。关键在于指定 CP_UTF8 作为代码页,并把 dwFlags 设为 0(避免加 BOM)。
- 输入必须是合法的 UTF-16 编码:
wchar_t*指向以L'\0'结尾的宽字符串 - 调用两次:第一次传
NULL获取目标缓冲区大小(单位是字节),第二次填入目标缓冲区 - 如果源字符串含非法代理对(如高位代理后无低位代理),
WideCharToMultiByte默认返回 0 并设GetLastError()为ERROR_NO_UNICODE_TRANSLATION - 示例片段:
int size = WideCharToMultiByte(CP_UTF8, 0, src, -1, nullptr, 0, nullptr, nullptr);<br>std::string utf8(size, '\0');<br>WideCharToMultiByte(CP_UTF8, 0, src, -1, &utf8[0], size, nullptr, nullptr);
Linux/macOS下用std::codecvt_utf8_utf16已弃用,改用std::wstring_convert也不推荐
C++11 引入的 std::codecvt_utf8_utf16 和配套的 std::wstring_convert 在 C++17 中被标记为 deprecated,C++20 彻底移除。它们在 GCC/Clang 上行为不一致,且无法处理非 BMP 字符(U+10000 及以上)的代理对——会静默截断或崩溃。
- 别再写
std::wstring_convert<:codecvt_utf8_utf16>></:codecvt_utf8_utf16>,编译器可能报错或运行时出错 - POSIX 系统更可靠的做法是用
iconv:打开"UTF-16LE"到"UTF-8"的转换描述符,注意字节序——Windows 的 UTF-16 通常是小端,但若输入带 BOM,需先跳过或检测 -
iconv要求输入是char*,所以得把wchar_t*先按字节 reinterpret_cast 成char*,长度乘 2
跨平台方案:用utf8cpp或手动遍历UTF-16码元
轻量级、头文件-only 的 utf8cpp(utf8.h)能安全处理 UTF-16 → UTF-8,且明确支持代理对。它不依赖系统 API,适合嵌入式或跨平台项目。
- 核心函数是
utf8::utf16to8,接受std::vector<uint16_t></uint16_t>或std::u16string(C++11 起),输出std::string - 它内部检查高位代理(0xD800–0xDBFF)后是否紧跟低位代理(0xDC00–0xDFFF),否则按单个码元处理,避免乱码
- 如果你不想引入外部头文件,可以手写转换逻辑:遍历
uint16_t序列,遇到高位代理就组合成 Unicode code point,再用标准 UTF-8 编码规则生成字节序列(U+0000–U+007F → 1 byte;U+0080–U+07FF → 2 bytes;U+0800–U+FFFF → 3 bytes;U+10000–U+10FFFF → 4 bytes) - 手写时最容易漏掉的是:高位代理单独出现时应编码为 U+FFFD(REPLACEMENT CHARACTER),而不是跳过或崩溃
常见错误:把wchar_t长度当字节数,或忽略字节序
wchar_t 在 Windows 是 16 位,在 Linux/macOS 通常是 32 位——这意味着你不能直接把 wchar_t* 当 UTF-16 处理。如果代码要跨平台,必须明确输入是 UTF-16 编码的 char16_t* 或 uint16_t*,而不是 wchar_t*。
- 错误写法:
std::string s(reinterpret_cast<const char>(wstr.c_str()), wstr.size() * sizeof(wchar_t))</const>—— 这只是二进制复制,不是编码转换 - 如果输入是文件读取的 UTF-16 数据,务必检查前两个字节是否为
0xFF 0xFE(LE BOM)或0xFE 0xFF(BE BOM),并据此调整字节序,否则代理对会被拆散 - 用
std::u16string替代std::wstring表达“UTF-16 字符串”,语义更清晰,也避免平台差异干扰
实际转换中最容易被忽略的,是输入数据的真实编码意图和字节序一致性——哪怕函数调用看起来成功,只要源数据没按预期解码,结果就是不可逆的乱码。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











