不能直接用substr,因为std::string::substr按字节截取,而utf-8中英文字符字节数不同(英文1字节、中文3字节),易切断多字节字符导致乱码或异常。

中英文混排截取为什么不能直接用 substr
因为 std::string::substr 按字节切,而中文 UTF-8 编码占 3 字节,英文 ASCII 占 1 字节。比如字符串 "Hello你好" 长度是 8 字节,但实际只有 5 个字符(5 个 Unicode 码点)。直接 s.substr(0, 5) 可能截断中文,产生乱码或 std::out_of_range。
用 std::u32string + std::mbstou32 转换后截取
这是最稳妥的 C++ 标准库方案:先把 UTF-8 字符串转成定长的 char32_t 序列,再按码点数截取,最后转回 UTF-8。
-
std::mbstou32将 UTF-8std::string转为std::u32string(每个元素对应一个 Unicode 码点) - 对
std::u32string调用.substr(0, n),n是目标字符数(非字节数) - 用
std::to_bytes(C++20)或手动调用std::c32rtomb转回 UTF-8std::string - 注意:输入必须是合法 UTF-8,否则
std::mbstou32返回 -1 或抛异常
示例(C++20):
#include <string>
#include <locale>
#include <codecvt>
std::string utf8_truncate(const std::string& s, size_t char_count) {
std::u32string u32 = std::wstring_convert<:codecvt_utf8>, char32_t>{}.from_bytes(s);
if (u32.length() , char32_t>{}.to_bytes(truncated);
}
</:codecvt_utf8></codecvt></locale></string>
更轻量:手写 UTF-8 字节偏移解析(无依赖、C++11 可用)
如果不想引入 std::codecvt(已弃用)或 C++20,可自己遍历 UTF-8 字节流,统计码点数并记录每个码点起始位置。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- UTF-8 单字节字符:以
0xxxxxxx开头(ASCII) - 多字节字符:首字节以
110xxxxx/1110xxxx/11110xxx开头,后续字节均以10xxxxxx开头 - 用
unsigned char读原字符串,跳过后续字节,每遇到一个首字节就计 1 个字符 - 截取时,只切到第
n个字符的起始字节位置,不破坏任何 UTF-8 序列
关键逻辑片段:
size_t utf8_char_length(const std::string& s, size_t max_chars) {
size_t pos = 0, chars = 0;
while (pos
<h3>容易被忽略的边界情况</h3>
<p>真正上线前必须检查这几点:</p>
- 输入为空或含非法 UTF-8 字节序列(如
\xFF\xFE),要决定是截断、跳过,还是报错 - Emoji 可能占多个码点(如带肤色修饰符的 ??),但用户通常希望按“视觉字形”算 1 个,此时需用 Unicode 图形簇(grapheme cluster)算法,标准库不支持,得用 ICU 或
utf8proc -
std::codecvt_utf8在 C++17 已标记为 deprecated,C++20 移除;新项目优先用 C++20std::text_encoding(尚未广泛实现)或第三方库 - 性能敏感场景下,反复转换开销大,建议缓存转换结果或预计算字符偏移表
混排截取不是简单长度运算,核心是明确「你定义的『一个字符』到底指什么」——码点?图形簇?还是传统 GBK 下的双字节?选错抽象层,后面全是坑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










