C++如何处理宽字符(wchar_t)和std::wstring?

C++宽字符(wchar_t)非通用Unicode类型,Windows用UTF-16(16位),Linux/macOS用UTF-32(32位),跨平台不可移植;应优先选用UTF-8编码的std::string或第三方库处理Unicode。

在C++中处理宽字符(wchar_t)和std::wstring,核心是理解其设计目的、平台差异与实际使用限制。它主要用于支持Unicode(但不是UTF-8),尤其在Windows API或需要处理本地化宽字符串的场景中;但在跨平台项目中,更推荐用char8_t + UTF-8 或第三方库(如utf8cpp、icu)。

宽字符不是“通用Unicode类型”

wchar_t的大小和编码含义由编译器与平台决定:

  • Windows(MSVC/MinGW):通常为16位,编码是UTF-16(可变长,含代理对)
  • Linux/macOS(GCC/Clang):通常为32位,编码是UTF-32(固定长度,一个wchar_t对应一个Unicode码点)

这意味着sizeof(wchar_t)不可移植,std::wstring也不能跨平台直接序列化或网络传输。别假设L"€".length() == 1——在Windows上它可能是2(代理对),在Linux上是1。

输入输出需匹配locale和流类型

std::wcoutstd::wcin前必须设置正确的全局locale,否则可能输出乱码或静默失败:

  • Windows下常用:std::locale::global(std::locale("")); // 使用系统区域设置
  • Linux/macOS下建议显式指定:std::locale::global(std::locale("en_US.UTF-8"))
  • 确保控制台/终端本身支持宽字符显示(例如Windows Terminal、iTerm2),否则wcout 可能只打印空格

与C风格字符串和API交互要小心转换

Windows API大量使用LPCWSTR(即const wchar_t*),而POSIX函数基本不接受wchar_t*。转换时避免手写循环:

  • std::wstring::c_str()获取只const wchar_t*,传给Windows函数(如CreateFileW
  • 从多字节转宽字符:Windows用MultiByteToWideChar(CP_UTF8, ...);Linux用mbstowcs()(注意目标缓冲区大小)
  • 反向转换(宽→多字节):Windows用WideCharToMultiByte(CP_UTF8, ...);Linux用wcstombs()

切勿用reinterpret_castchar*wchar_t*间强制转换——编码不同,字节布局不兼容。

替代方案更实用

除非必须对接Windows GUI或COM接口,否则优先考虑:

  • 内部统一用UTF-8编码的std::string(C++20起可用std::u8string
  • std::text_encoding(C++26草案)或成熟库(如utf8cpp)做编码验证与转换
  • 读写文件时明确指定编码(如用std::ofstream配合std::codecvt_utf8_utf16已弃用,改用std::from_chars/std::to_chars或第三方)

宽字符体系在C++标准中功能有限、平台行为分裂,现代项目中它更多是“遗留互操作层”,而非首选文本抽象。