不能直接用substr截断utf-8字符串,因为它按字节而非字符计数,会导致中文乱码且忽略省略号占位;安全做法是手动解析utf-8序列,统计unicode字符数,预留空间后拼接"..."。

截断字符串时为什么不能直接用 substr?
因为 substr 只按字节/字符数切,对 UTF-8 中文会截出乱码(比如把“你好”切成“你”),而且不考虑省略号("...")占位——你预留 10 个字符,结果加上三个点就超了。
真正安全的做法是:先计算能放多少个可见字符,再手动拼接。C++11 起标准库没提供 Unicode 字符计数,所以得靠 std::string 的 UTF-8 编码特性逐字节解析。
- UTF-8 单个汉字占 3 字节,但
size()返回的是字节数,不是字符数 - 省略号本身要占空间,如果原字符串刚好够显示
n个字符,加"..."就必须砍掉至少 3 个字符 - 最简方案:从头遍历,累计 UTF-8 字符个数,到上限前停下
用 std::string 手动解析 UTF-8 字符实现截断
核心逻辑是识别 UTF-8 多字节序列首字节(0xC0–0xFF),跳过后续字节。下面这个函数返回最多 max_chars 个 Unicode 字符的子串,末尾自动加 "..."(如果被截断):
std::string truncate_utf8(const std::string& s, size_t max_chars) {
if (s.empty()) return s;
size_t char_count = 0;
size_t pos = 0;
while (pos = s.size()) return s;
return s.substr(0, pos) + "...";
}
注意:max_chars 是目标字符数(不是字节数),函数内部只处理合法 UTF-8;遇到非法字节直接返回原串,避免崩溃。
用 std::wstring_convert(已弃用)还是该避开?
别用。C++17 起 std::wstring_convert 和 std::wbuffer_convert 已被标记为 deprecated,且 Windows 上 std::codecvt_utf8 实现不全,Linux 下也常出错。硬转 std::wstring 再截取,反而增加依赖和不可控行为。
- 跨平台项目里,纯 UTF-8 字节操作更可靠
- 如果已有 ICU 或 Boost.Locale,可以用它们的
utf8::next或utf8::distance,但引入重量级依赖不值得只为截断 - 简单项目坚持手动解析,50 行内搞定,无外部依赖
性能和边界情况怎么处理?
这个手动解析在大多数场景下足够快(O(n) 且 n 很小),但要注意几个坑:
- 空字符串、全 ASCII 字符串、超长字符串(>1MB)都需测试,避免
pos溢出或循环太久 - 如果
max_chars == 0,直接返回"...";如果max_chars == 1,最多只能放一个字符 +"...",此时总长至少 4 字节,要确保原串非空 - 省略号用英文点还是中文省略号(
"……")?后者占 2 字符,但视觉更统一——选哪个取决于 UI 规范,代码里替换字符串即可
真正麻烦的是混合中英文+标点+emoji 的字符串,比如 “Hello ? 你好!”,其中 ? 是 4 字节 UTF-8 字符,手动解析必须支持 4 字节序列,上面示例已覆盖。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











