std::string::size() 返回字节数而非字符数,因 utf-8 为变长编码;需统计首字节(0xxxxxxx、110xxxxx 等)得到真实字符数,推荐用 unsigned char 遍历判断,避免 std::codecvt_utf8 等弃用方案。

为什么 std::string::size() 不能直接当字符数用
因为 UTF-8 是变长编码:ASCII 字符占 1 字节,中文、emoji 等通常占 3 或 4 字节。调用 s.size() 返回的是字节数,不是 Unicode 码点个数。比如字符串 "你好" 在 UTF-8 下是 6 字节,但只有 2 个字符。
用 std::u8string + std::count_if 判断首字节最稳妥
UTF-8 编码有明确的首字节规则:0xxxxxxx(1 字节)、110xxxxx(2 字节首)、1110xxxx(3 字节首)、11110xxx(4 字节首)。只要统计这些“首字节”数量,就是字符个数。
实操建议:
- 确保输入是合法 UTF-8(否则统计无意义,可先用轻量校验)
- 用
unsigned char遍历,避免符号扩展干扰判断 - 检查首字节:(
byte & 0b10000000) == 0 → ASCII;(byte & 0b11100000) == 0b11000000 → 2 字节首;以此类推
示例片段:
int utf8_char_count(const std::string& s) {
int count = 0;
for (unsigned char b : s) {
if ((b & 0b10000000) == 0) count++; // 0xxxxxxx
else if ((b & 0b11100000) == 0b11000000) count++; // 110xxxxx
else if ((b & 0b11110000) == 0b11100000) count++; // 1110xxxx
else if ((b & 0b11111000) == 0b11110000) count++; // 11110xxx
}
return count;
}
别依赖 std::codecvt_utf8 —— 它在 C++17 被弃用且不可靠
很多老教程推荐用 std::wstring_convert + std::codecvt_utf8 转成 wchar_t 再算长度,但问题很多:
-
std::codecvt_utf8在 C++17 标准中标记为 deprecated,GCC/Clang 新版本默认不启用 -
wchar_t在 Windows 是 UTF-16,在 Linux 通常是 32 位,转码行为不一致 - 遇到代理对(surrogate pair)或未配对的 UTF-16 单元时容易出错
需要更健壮?用 ICU 或 utf8cpp 库
自己手写首字节计数够用,但如果要处理错误恢复、BOM、组合字符(如带重音的 é)、或验证合法性,别硬刚:
-
utf8cpp(header-only)提供utf8::distance,内部做完整 UTF-8 解码和校验 - ICU 的
u_strlen()+u_strToUTF8()更重,但支持国际化边界分析(如按用户感知的“字”切分) - 注意:所有第三方库都要求输入是合法 UTF-8;若来源不可控,先做一次轻量校验(比如检查是否有多余的 continuation 字节)
真正容易被忽略的点是:你拿到的 std::string 是否真的存的是 UTF-8?如果来自文件读取、网络响应或用户输入,得确认编码来源,而不是默认它合规。否则计数结果可能在某些边缘 case(如截断的 emoji)上完全错乱。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











