手动切分 std::string 时需按 Unicode 字符显示宽度而非字节切分,否则中文、emoji 显示错乱;应先转 wchar_t 再用 wcwidth 测宽,折行宜从右往左找空格或标点断点,并返回每行字符串的 vector。

用 std::string 手动切分时,别忽略 Unicode 字符宽度
直接按字节切分 std::string 在含中文、emoji 的场景下会显示错乱,因为 UTF-8 中一个汉字占 3 字节,但显示宽度是 2 列(多数终端下),而 ASCII 字符宽度是 1。C++ 标准库不提供字符显示宽度计算,得自己处理或依赖外部库(如 icu 或轻量级的 utf8cpp)。若只处理 ASCII + 常见中文,可先用 std::mbstowcs 转宽字符,再用 std::wcwidth 逐字符测宽——注意 std::wcwidth 对 UTF-8 输入无效,必须先转成 wchar_t 序列。
实现折行逻辑:从右往左找断点比从左往右更稳妥
从左往右扫描容易在单词中间硬切(比如把 “internationalization” 切成 “inter…”,破坏可读性);从右往左找第一个空格或标点,能更好保留语义单元。实际操作中:
- 对每行候选区间
[start, start + width],先检查是否已到字符串末尾 - 否则从
start + width - 1往前遍历,找最近的' '、'\t'、'\n'或中文标点(如,。) - 若没找到,则退回到
start + width处强制截断(需标记溢出提示,如"…") - 记得跳过开头的空白,避免空行或缩进错位
std::stringstream 和 std::getline 不适合做列宽控制
它们按换行符或分隔符拆分,和列宽无关。有人误用 std::setw 配合 std::left 输出,但这只影响单行填充,不触发换行。真正要的是“输入一段文本 → 输出多行字符串向量”,所以核心函数应返回 std::vector<:string></:string>,每项是一行内容。示例骨架如下:
std::vector<std::string> wrap_text(const std::string& s, size_t width) {
std::vector<std::string> lines;
size_t start = 0;
while (start < s.length()) {
size_t end = std::min(start + width, s.length());
// … 实际宽度计算与断点查找逻辑
lines.push_back(s.substr(start, end - start));
start = end;
}
return lines;
}
Windows 控制台默认不支持 UTF-8,std::cout 输出中文会乱码
即使字符串本身正确,Windows 上未设置代码页就直接输出 UTF-8 字节流,会导致中文显示为 。必须在程序开头加:
立即学习“C++免费学习笔记(深入)”;
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
或者用 _setmode(_fileno(stdout), _O_U16TEXT) 配合 std::wcout,但这就要求全程用 std::wstring。更现实的做法是:只在 Linux/macOS 做完整 UTF-8 宽度处理,Windows 下降级为字节长度截断(并提醒用户终端编码需设为 UTF-8)。
最麻烦的不是算法,而是“宽度”定义本身:不同字体、不同终端、不同 emoji 版本,同一个字符的显示宽度可能不同。生产环境建议优先用成熟排版库,自己实现时务必在目标终端上实测几组中英文混排样例。


















