Python shorten 的空白折叠:文字还没超长,为什么换行已经不见了
给日志列表生成一行预览时,原文只有十几个字符,宽度设成一百,换行和连续空格却仍然消失。检查长度没有发现截断,容易误以为数据在读入时损坏。实际变化可能发生在 textwrap.shorten:它先整理空白,再判断是否需要缩短,即使最后无需省略任何词,也会返回整理后的文字。
这适合把普通段落压成紧凑预览,却不适合保留诗歌分行、命令缩进或逐字证据。应同时保留原文和展示摘要,明确哪份可以改变格式。下面使用英文短词,是为了让空白位置、长度与输出都能直接手算;示例不涉及中文分词或字体测量。
可直接运行的对照实验
将代码保存为 demo.py,运行 python demo.py。输入显式包含制表符、换行和首尾空格。先给足够大的宽度,再逐步收紧,最后故意提供过长的占位符,验证正常结果与失败边界。所有操作都在内存中完成。
AI概念插图:松散的文字单位经过收拢后形成紧凑预览,末尾保留省略位置。图片不是运行截图,也不表示实测性能。
from textwrap import shorten
original = " red\t\tgreen\nblue "
normalized = shorten(original, width=16, placeholder="...")
assert normalized == "red green blue"
print("normalized:", repr(normalized))
unchanged_options = shorten(original, width=16, placeholder="...",
replace_whitespace=False, expand_tabs=False)
assert unchanged_options == normalized
assert original == " red\t\tgreen\nblue "
print("options cannot restore whitespace:", unchanged_options == normalized)
short = shorten(original, width=10, placeholder="...")
assert short == "red..."
assert len(short) <= 10
print("width 10:", short)
assert shorten("red green", width=3, placeholder="...") == "..."
assert shorten("red", width=3, placeholder="...") == "red"
assert shorten(" ", width=3, placeholder="...") == ""
try:
shorten("red green", width=2, placeholder="...")
except ValueError:
print("placeholder too wide: ValueError")
else:
raise AssertionError("expected invalid width")宽度够用,也不代表逐字保留
第一行输出 normalized: 'red green blue',其中各词之间只剩一个普通空格。第二次把 replace_whitespace 与 expand_tabs 都关闭,结果仍然相同,因为 shorten 在进入内部换行器之前已完成空白折叠。这些参数无法把已经丢掉的原始格式恢复回来。
程序另存 original 并断言它没有变化,说明函数返回了新的展示文字,没有原地修改字符串。若数据库中原文也变短,应沿调用链检查是不是把返回值写回了原文字段。为 preview 与 original 使用不同变量和存储字段,可以减少这种误用。
省略标记也占预算
宽度十时结果是 red...,不是简单截取前十个字符。函数会从末尾舍弃词,使保留部分和 placeholder 一起满足预算;本例自定义三个点,紧接最后保留的词。宽度三时仅留下三个点,表明摘要可能完全不含原词,调用方需要接受这种结果。
占位符本身放不下时会抛出 ValueError。不要捕获后直接返回不受限制的原文,否则本来依赖长度限制的界面又会溢出。可以提前检查配置,或者给出明确的最小宽度,但应把这一规则写进接口,而不是在异常后猜一个结果。
按用途选择缩短策略
如果业务要求严格保留前若干字符,应该选择与字符单位相符的切片方案;如果要求保留完整词组,则要先建立词组和断点规则。shorten 不理解姓名、型号或中文语义,也不负责判断被舍弃的句尾是否包含否定、例外或重要期限,不能把生成预览等同于可靠摘要。
宽度按字符串字符处理,并非终端显示列数或网页像素。组合字符、宽字符与不同字体都可能让同样长度显示得不同。此处仅验证函数的文本结果;真正用于窄栏时,应再用目标字体检查展示,并提供展开原文的入口,让用户可以确认完整信息。
验收至少保留四类样本:无需缩短却包含格式空白、恰好达到宽度、必须丢词,以及占位符本身过长。把预期文本写成精确断言,比只检查长度更有用;否则结果虽然没有超限,换行丢失和内容完全被省略仍可能被漏掉。
资料核对日期:2026年10月2日(北京时间)。代码在 CPython 3.12.14 中独立运行。


