最稳方案是用multibytetowidechar指定cp936代码页转换,避免依赖locale或控制台编码;std::wstring_convert已废弃且gcc未实现,std::wcout易因编码不匹配输出乱码。

Windows下用MultiByteToWideChar转string到wstring最稳
直接调std::wstring_convert在VS2015之后就废弃了,GCC里压根没实现过,别碰。Windows平台默认编码是GBK(CP936),MultiByteToWideChar能明确指定代码页,不依赖locale设置,也不怕控制台乱码干扰。
常见错误现象:用std::wcout 输出空白或问号;<code>wstring内容看起来像乱码但长度对得上——大概率是UTF-8字节被当成了Latin-1解码。
- 先确认源
string是GBK编码(比如从文件读、控制台输入、WinAPI返回) - 调用
MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, nullptr, 0)获取目标缓冲区大小 - 分配
std::vector<wchar_t></wchar_t>,再调一次填入数据,最后构造std::wstring - 如果源是UTF-8(比如网络JSON、Linux跨平台代码),把
CP_ACP换成CP_UTF8
Linux/macOS下用std::codecvt_utf8<wchar_t></wchar_t>要小心locale
这个转换器只支持UTF-8 ↔ UTF-32(wchar_t在Linux是4字节),不能处理GBK/Big5。而且std::codecvt系列在C++17已被弃用,新项目尽量避免。
使用场景有限:你确定输入是UTF-8,且只在glibc环境跑,setlocale(LC_ALL, "")已正确设置(比如LANG=zh_CN.UTF-8)。
立即学习“C++免费学习笔记(深入)”;
- 别直接用
std::wstring_convert<:codecvt_utf8>></:codecvt_utf8>——它内部依赖locale facet,而glibc的std::codecvt_byname对中文locale支持不稳定 - 更可靠的做法:用
iconv()系统调用,显式指定"UTF-8"→"WCHAR_T" - 如果只是临时调试,
std::mbstowcs可应急,但它依赖当前C locale,setlocale(LC_CTYPE, "zh_CN.UTF-8")必须在转换前调用
std::string存中文时,别假设单字符=单字
UTF-8里一个汉字占3字节,str[0]取出来的是第一个字节,不是“第一个字”。str.length()返回字节数,不是字数。这是所有后续转换出错的根源。
容易踩的坑:for (int i = 0; i 会把汉字拆成3个乱码字节输出;用<code>s.substr(0, 2)可能切在汉字中间,导致后续解码失败。
- 需要按Unicode字符遍历时,用
std::u8string(C++20)配合std::mbrtoc8,或第三方库如utf8cpp - 简单场景下,先转成
std::wstring再用wstr.size()和wstr[i]——这时才是真正的“第i个字符” - 文件读写务必明确编码:用
std::ifstream打开文本时加std::ios::binary,自己处理BOM和转换,别依赖流的自动编码推断
跨平台项目统一用UTF-8 + std::string,wstring只用于WinAPI交互
Windows控件(CreateWindowEx、SetWindowTextW)、COM接口、部分WinSDK函数强制要求LPCWSTR。除此之外,现代C++项目尽量让std::string承载UTF-8字节流,避免在各处反复转换。
性能影响明显:每次string↔wstring都要遍历、查表、分配内存;wstring在Linux占4倍空间(wchar_t=4B),Windows是2B(UCS-2子集),同一份代码行为不一致。
- 对外接口(网络、文件、CLI参数)全部用UTF-8
std::string - 仅在调用WinAPI前一刻,用
MultiByteToWideChar(CP_UTF8, ...)转一次;返回后立刻转回 - CMake里加
add_compile_options(-finput-charset=UTF-8 -fexec-charset=UTF-8),避免源码中文字符串被编译器误判编码
string到底是什么编码、从哪来、要到哪去。










