std::string::length() 返回字节数而非 Unicode 字符数,因 UTF-8 中中文占 3 字节,故"你好"长度为 6 而非 2;应使用 utf8cpp 等库按码点计数。

为什么 std::string::length() 不能直接当“中文字符数”用
因为 UTF-8 编码下,一个中文字符占 3 个字节,std::string::length() 返回的是字节数,不是 Unicode 码点数。比如 "你好" 的 length() 是 6,但你想要的“长度”是 2。
直接按字节遍历或用 std::wstring 也不行:Windows 默认宽字符是 UTF-16(可能有代理对),Linux/macOS 是 UTF-32(看似简单但不跨平台),且 std::wstring 不自带 UTF-8 解码逻辑。
- 别用
str.size()或strlen()当“字符个数” - 别假设
wchar_t在所有平台都一一对应一个汉字(UTF-16 下“?”需要两个wchar_t) - 别手动查字节前缀(如
0xE0–0xEF)却不验证后续字节有效性——会把损坏的 UTF-8 当作合法中文
用 std::mbstowcs() + std::wcslen() 快速估算(仅限本地 locale 支持 UTF-8)
如果确认程序运行环境的 locale 已设为 UTF-8(如 Linux/macOS 默认,Windows 需调用 setlocale(LC_ALL, "en_US.UTF-8") 或 "Chinese_China.65001"),可借助 C 标准库的多字节转换:
std::string s = "Hello世界"; std::vectorbuf(s.size() + 1); // 保守预分配 size_t len = std::mbstowcs(buf.data(), s.c_str(), buf.size()); if (len != static_cast (-1)) { int char_count = static_cast (len); }
注意:mbstowcs 依赖当前 locale,Windows 上若 locale 不匹配(比如仍是 GBK),会把 UTF-8 字节流误判为非法,返回 -1 或截断。
立即学习“C++免费学习笔记(深入)”;
- 必须在调用前确保
setlocale()成功,且返回值非nullptr -
mbstowcs遇到非法 UTF-8 字节序列会停止转换,返回已转换数 —— 无法区分“全成功”和“中途出错” - 不推荐用于不可信输入(如网络字符串),因容错性差
用 utf8cpp 库逐码点遍历(推荐,轻量、跨平台、健壮)
最稳妥的方式是使用专为 UTF-8 设计的解析库,utf8cpp(头文件-only,无依赖)能正确识别并跳过每个 UTF-8 码点,包括 emoji 和生僻汉字:
#includestd::string s = "a你?好"; int count = 0; for (auto it = s.begin(); it != s.end(); ) { if (utf8::next(it, s.end()) != 0) ++count; }
它内部严格校验 UTF-8 格式(如超长编码、高位错误、代理对禁止等),遇到非法字节会抛 utf8::invalid_code_point 异常(可捕获处理)。
- 支持 C++11 及以上,无需编译,直接 include 头文件即可
- 比手写状态机更可靠;比 ICU 轻量百倍,适合嵌入式或工具链受限场景
- 若需同时获取码点值(如过滤控制字符),可用
utf8::next(it, end, cp)提取uint32_t cp
避免踩坑:Windows 控制台输出与长度计算不是一回事
即使你算出了正确的中文字符数(比如 5),用 std::wcout 仍可能显示错位或乱码——这是终端编码、字体、以及 Windows 的 SetConsoleOutputCP(CP_UTF8) 设置问题,和字符串长度计算无关。
换句话说:长度计算只管“有多少个 Unicode 字符”,不解决“怎么显示出来”。两者要分开处理。
- 长度逻辑一律基于 UTF-8 字节流解析,不依赖终端能力
- 输出问题请单独检查
chcp 65001、控制台字体是否含 CJK、以及是否调用SetConsoleOutputCP - 日志写文件时,UTF-8 字符串可直接保存,无需转宽字符——文件内容长度 ≠ 显示宽度
utf8cpp 的 utf8::distance() 就是最省心的选择;若环境受限无法引入第三方,务必先验证 locale 设置再用 mbstowcs,否则静默出错比报错更难排查。










