std::string 存 utf-8 时 .length() 返回字节数而非字符数,如 "café" 返回 5;直接按字节索引截取或遍历会破坏 utf-8 编码,导致非法字节序列。

std::string 能存 Unicode,但不是“字符容器”——它只管字节,不管编码。你得自己清楚存的是 UTF-8,并避开所有假设“一个 char 对应一个字符”的操作。
用 std::string 存 UTF-8 时,.length() 返回的是字节数,不是字符数
这是最常踩的坑:"café" 在 UTF-8 中是 5 个字节(é 占两字节),.length() 就返回 5。如果你按索引截取或遍历,很容易切到半个字符里去。
- 别写
s.substr(0, 1)想取第一个字符——可能得到非法 UTF-8 字节序列 - 别用
for (size_t i = 0; i 遍历“字符”——<code>c是单个字节,不是码点 - 需要字符计数?用
utf8::distance(s.begin(), s.end())(来自 utfcpp)或手动解析 UTF-8 头字节 - 需要逐码点处理?先转成
std::vector<char32_t></char32_t>,再操作
std::u16string 和 std::u32string 不等于“Unicode 安全”,只是存储单元不同
std::u16string 存的是 UTF-16 编码字节对,std::u32string 存的是 UTF-32 码点。但标准库不帮你解码,也不检查代理对是否合法。
-
u"?".size()返回 2(UTF-16 代理对),不是 1;U"?".size()才是 1 -
std::u16string不能直接用于 Windows API 的CreateFileW—— 得确保它是合法 UTF-16(无孤立代理) -
std::u32string最直观:每个char32_t对应一个 Unicode 码点,适合内部逻辑(如正则、大小写转换) - 别把
std::u16string当作“比std::string更高级的 Unicode 字符串”——它没自带任何 UTF-16 解码能力
转换必须显式做,C++ 标准库不提供跨编码自动转换
从文件读 GBK、从网络收 UTF-8、调 Windows API 需 UTF-16……这些转换不会自动发生,也不会隐式构造。
- 别写
std::wstring s = u8"你好";—— C++ 不允许这种隐式转换,编译失败 - 用 utfcpp:
utf8::utf8to32(s.begin(), s.end(), back_inserter(u32vec)) - 用系统 API:
MultiByteToWideChar(CP_UTF8, ...)(Windows)、iconv()(Linux/macOS) - 读配置文件前先检测编码(比如用
std::text_encoding::detect(),C++26)再转 UTF-8
char8_t 和 std::u8string 是 C++20/C++26 的类型安全升级,但不解决语义问题
char8_t 不是“UTF-8 char”,它只是个独立类型;std::u8string 不是“UTF-8 安全字符串”,它还是 std::basic_string<char8_t></char8_t>,照样能存无效字节。
- 写
u8"你好"→ 类型是const char8_t[6],内容是 UTF-8 字节,类型更清晰了 - 但
std::u8string s = u8"你好"; s += "\xFF";依然合法——你得自己验证有效性(utf8::is_valid()) - 遍历
std::u8string仍是按字节遍历,不是按码点;要按码点遍历还得用 utfcpp 的utf8::begin() - C++26 的
std::text_encoding可帮你判断编码,但不替你转
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











