手动按固定宽度截断字符串需区分ascii与utf-8:ascii可直接substr切分,utf-8须用mbrlen等定位合法边界;换行后补空格需手动拼接并处理尾空格;视觉宽度需考虑全角字符与制表符,不能仅依赖length()。

字符串按固定宽度截断并换行,用 std::string 手动切分最可控
标准库没有内置的“自动换行”函数,std::string 本身也不带排版逻辑。必须自己按字符数(注意:是字节数还是 Unicode 码点?)切分。对 ASCII 文本,按字节切即可;含中文时,若用 UTF-8 编码,一个汉字占 3 字节,直接按字节截会破坏编码——所以**先确认输入是否纯 ASCII 或已按 UTF-8 安全处理**。
常见错误是用 substr(0, width) 循环截取,但没检查截断点是否落在多字节字符中间,导致输出乱码或 std::out_of_range 异常。
- 只处理 ASCII 或已知单字节编码时,可直接按
width截取:s.substr(i, width) - 处理 UTF-8 字符串,需用
std::mbrlen或第三方库(如utf8cpp)定位合法边界 - 换行前检查当前位置是否为空格/标点,避免把单词从中间劈开(可选优化)
左对齐换行后每行补空格到固定宽度,用 std::setw 和 std::left 不适用
std::setw 只对单次 生效,不能让换行后的每一行都补空格到等宽。真要实现“每行都是 <code>width 长度”,得手动拼接:
std::string line = s.substr(i, width); line += std::string(width - line.length(), ' ');
但要注意:如果原字符串末尾不足 width,补空格没问题;但如果某行在中间遇到换行符(比如用户输入含 '\n'),应优先尊重原始换行,而不是强行塞满。
- 补空格前先
line.erase(line.find_last_not_of(" \t"), std::string::npos)去尾部空格,再补,避免叠加 - 对齐方向影响补空格位置:左对齐补右边,右对齐补左边,居中则两边都补
- 别用
printf("%-*s", width, line.c_str())混用 C 风格,容易和 C++ 流缓冲冲突
实际输出到终端时,制表符、全角字符会让视觉宽度错乱
终端显示宽度 ≠ 字符数。一个 ASCII 字符占 1 格,一个中文字符(Unicode 全角)占 2 格,'\t' 可能跳 4 或 8 格。如果目标是“视觉上每行不超过 N 列”,就不能只数 std::string::length()。
典型现象:代码算出 20 字符一行,但含 5 个汉字后,终端显示溢出——因为 15 ASCII + 5 中文 ≈ 15×1 + 5×2 = 25 列。
- 需要宽度感知函数,例如 Linux 下可用
wcswidth()(需先转std::wstring) - Windows 控制台对 Unicode 支持有限,
SetConsoleOutputCP(CP_UTF8)必须提前调用 - 简单方案:约定输入不含全角字符,或要求调用方传入“视觉宽度数组”代替原始字符串
封装成可复用函数时,避免隐式拷贝和重复分配
频繁调用换行函数时,std::vector<:string></:string> 返回值容易触发多次内存分配。如果调用方只遍历结果,不如接受一个回调函数:
void wrap_text(const std::string& s, size_t width,
std::function<void std::string> line_handler) {
for (size_t i = 0; i <p>这样既避免中间存储,又支持流式处理(比如边生成边写文件)。</p>
<ul>
<li>不要返回 <code>std::vector</code> 除非明确需要随机访问</li>
<li>传入 <code>const std::string&</code> 而非值,防止意外拷贝</li>
<li>如果 <code>width == 0</code>,必须提前检查并报错,否则 <code>min(i + 0, ...)</code> 导致死循环</li>
</ul>
<p>真正麻烦的从来不是切几刀,而是搞清“宽度”到底指字节数、字符数,还是终端列数——这三个值在真实文本里几乎从不相等。</p></void>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











