合理的 word wrap 是在空格或标点处断行、不拆单词、每行长度不超过指定宽度的文本折行方式;需逐词扫描并累积行缓冲,对中文等需按显示宽度(非字节长度)计算,Unicode 处理需解码或借助 Boost.Text 等库。

什么是合理的 word wrap?不是简单按字符截断
直接用 substr 每 n 个字符切一刀,会把单词从中间劈开,比如 "hello world" 宽度设为 5,就可能变成 "hell\no wo\nrld" —— 这不是人想要的折行。真正的 word wrap 要在空格(或标点)处断行,同时保证每行 ≤ 指定宽度,且不拆单词(除非单个单词本身超长)。
手动实现核心逻辑:逐词扫描 + 行缓冲累积
标准库没提供现成函数,得自己写。关键思路是把字符串按空格切分成词(std::istringstream 或 std::string::find_first_of),再逐个尝试加入当前行:
- 若当前行为空,直接加词
- 否则检查:
current_line.length() + 1 + next_word.length() (+1 是空格位) - 满足就追加空格+词;不满足就 push 当前行,重置 current_line 为 next_word
- 遇到超长词(
word.length() > width),只能强制折断——用word.substr(0, width)和剩余部分递归处理
示例片段:
std::vector<std::string> wrap(const std::string& s, size_t width) {
std::vector<std::string> lines;
std::istringstream iss(s);
std::string word, line;
while (iss >> word) {
if (line.empty()) {
line = word;
} else if (line.length() + 1 + word.length() <= width) {
line += " " + word;
} else {
lines.push_back(line);
line = word;
}
}
if (!line.empty()) lines.push_back(line);
return lines;
}
处理中文、全角字符和混合文本时的坑
C++ 的 std::string::length() 返回字节数,不是显示宽度。中文字符通常是 UTF-8 编码(3 字节),但显示占 2 个英文字符宽度。如果按字节算宽度,一行最多放 20 字节 ≠ 最多放 20 个英文字符 ≠ 最多放 ~6 个汉字。
立即学习“C++免费学习笔记(深入)”;
- 若需按「显示宽度」折行(如终端输出),必须先做 UTF-8 解码,用
std::mbstowcs或第三方库(如 ICU、utf8cpp)计算每个字符的列宽 - 简单方案:假设所有非 ASCII 字符占 2 列,ASCII 字符占 1 列,手写宽度计算器
- 错误写法:
if (line.length() + ... <= width)—— 这对中文完全失效 - 更糟的是:某些字体里中文标点(,。!)也占 2 列,但英文标点只占 1 列
要不要用 Boost 或 fmt?权衡点在哪
Boost.Text 提供了 boost::text::wrap,支持 Unicode 和自定义断行策略,但引入整个 Boost 太重;fmt::format 没内置 wrap 功能,别被名字误导。
- 纯 C++17/20 项目,没外部依赖 → 手写上面逻辑最可控
- 已用 Boost 且需要处理日文/韩文/双向文本 →
boost::text::wrap值得接入 - 只是临时调试打印 → 用 Python 脚本预处理反而更快
- 注意:Boost.Text 不是 header-only,需编译链接,CMake 中要显式 find_package(boost_text)
真正难的不是切分,而是宽度定义本身——你得先说清“宽度”指字节、Unicode 码点,还是终端列数,再决定怎么算。


















