Windows下std::wstring为UTF-16,须用MultiByteToWideChar转UTF-8,用WideCharToMultiByte转回;禁用std::codecvt_utf8(已弃用);推荐utf8cpp或iconv跨平台方案。

UTF-8转std::wstring(Windows下用MultiByteToWideChar)
在Windows平台,std::wstring底层是UTF-16编码,不能直接用std::string赋值中文UTF-8字符串。常见错误是直接构造:std::wstring wstr(str.begin(), str.end())——这会把每个UTF-8字节当一个宽字符,结果全是乱码。
正确做法是调用系统API做编码转换:
-
MultiByteToWideChar(CP_UTF8, 0, utf8_c_str, -1, nullptr, 0)先获取目标缓冲区长度(含结尾L'\0') - 分配
std::vector<wchar_t></wchar_t>或std::wstring缓冲区 - 再调用一次
MultiByteToWideChar写入数据 - 注意:返回值为0表示失败,需检查
GetLastError(),常见错误码ERROR_NO_UNICODE_TRANSLATION说明输入含非法UTF-8序列
std::wstring转UTF-8(Windows下用WideCharToMultiByte)
反向转换同样不能靠强制类型转换或逐字符拼接。直接用std::string s(wstr.begin(), wstr.end())会把每个UTF-16码元当ASCII字节存,中文全丢。
必须用WideCharToMultiByte:
立即学习“C++免费学习笔记(深入)”;
-
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr)先查长度 - 分配
std::vector<char></char>缓冲区(大小为上一步返回值,不含结尾'\0'也可,但-1参数要求留空间) - 第二次调用填入数据,结果是标准UTF-8字节流
- 若
wstr含代理对(surrogate pair,即U+10000以上字符),该API默认支持,无需额外处理
跨平台方案:用std::codecvt_utf8(已弃用,但仍有项目依赖)
C++11引入的std::codecvt_utf8<wchar_t></wchar_t>曾被用于转换,但C++17起标记为deprecated,GCC/Clang新版本编译会警告,MSVC部分版本已移除。不建议新项目使用。
如果维护旧代码,需注意:
- 它只支持UTF-8 ↔ UTF-32(
std::wstring在Linux/macOS是UTF-32,与Windows不同!) - 用法示例:
std::wstring_convert<:codecvt_utf8>> conv; std::string u8 = conv.to_bytes(L"你好");</:codecvt_utf8> - 实际行为高度依赖编译器和STL实现,同一段代码在Windows和Linux可能产出不同结果
更可靠的选择:第三方库如utf8cpp或iconv
想彻底避开平台差异和标准库坑,推荐轻量级库:
-
utf8cpp(头文件-only):提供utf8::utf8to16和utf8::utf16to8,输出是std::u16string,可与std::wstring互转(Windows下需注意sizeof(char16_t) == sizeof(wchar_t)成立,但语义不同) -
iconv(POSIX标准):跨平台稳定,但需链接-liconv,Windows需MinGW或单独编译;调用前必须iconv_open("UTF-8", "UTF-32LE")等明确指定编码名 - 关键提醒:所有转换函数都假设输入字节流合法。若源字符串来自网络或文件,务必先用
utf8::is_valid(utf8cpp)或手动校验首字节范围,否则崩溃或静默截断很常见
真正麻烦的不是转换本身,而是搞清当前std::wstring到底代表UTF-16还是UTF-32,以及你调用的API是否隐式做了BOM处理或换行符归一化。


















