textwrap模块 — 文本填充与包装

Python标准库精讲专题 · 文本处理篇 · 掌握文本格式化排版工具

专题:Python标准库精讲系统学习

关键词:Python, 标准库, textwrap, 文本填充, 文本包装, wrap, fill, dedent, indent, shorten, TextWrapper

一、textwrap模块概述

textwrap是Python标准库中专门用于文本格式化与排版的模块,提供了一系列高层函数和一个底层类TextWrapper,用于将文本段落按照指定宽度进行自动换行、填充、缩进处理。该模块在终端输出格式化、代码文档排版、文本报告生成等场景中有着广泛的应用。

核心定位:textwrap是Python文本处理工具箱中的"排版助手",专注于解决"如何让文本在固定宽度下优雅展示"这一核心问题。

stringre等模块不同,textwrap不关注文本内容本身,而是关注文本的呈现形式。它擅长的操作包括:将长段落按指定宽度换行、统一去除多行字符串的缩进、为文本块统一添加缩进前缀、将长文本截断为摘要等。

textwrap模块的设计哲学是"简单易用"。五个顶层函数(wrapfillshortendedentindent)覆盖了绝大多数使用场景,当需要更精细的控制时,可以通过TextWrapper类的构造参数获得全面的定制能力。

适用场景一览

模块导入:import textwrap 即可使用所有功能,无需额外安装第三方库。

二、核心函数详解

textwrap模块提供了五个顶层便捷函数,每个函数在内部都创建了一个TextWrapper实例并调用相应方法。这些函数足以覆盖日常开发中绝大多数的文本格式化需求。

2.1 wrap() — 返回单词列表

textwrap.wrap(text, width=70)将输入文本按照指定宽度进行换行,返回一个字符串列表,每个元素对应一个换行后的段落行。这是textwrap模块最基础的函数,其他大多数函数都依赖于它。

import textwrap text = "Python是一种广泛使用的高级编程语言,属于通用型编程语言。由吉多·范罗苏姆创造,第一版发布于1991年。" lines = textwrap.wrap(text, width=20) for i, line in enumerate(lines, 1): print(f"{i}: {line}") # 输出: # 1: Python是一种广泛使用 # 2: 的高级编程语言,属 # 3: 于通用型编程语言。 # 4: 由吉多·范罗苏姆创 # 5: 造,第一版发布于 # 6: 1991年。

注意,wrap()默认在空格处分词,对于中文文本(无空格分隔),会在宽度边界处截断字符。如果需要确保中文词汇完整性,可以结合第三方中文分词库预处理。

2.2 fill() — 返回单个字符串

textwrap.fill(text, width=70)本质上是wrap()的便捷封装:先调用wrap()获取换行列表,然后将列表元素用换行符\n拼接成一个字符串。适用于直接输出格式化后的段落文本。

import textwrap text = "textwrap.fill函数非常适合直接输出格式化段落,它内部调用了wrap函数并自动拼接换行符。" output = textwrap.fill(text, width=16) print(output) # 输出: # textwrap.fill函 # 数非常适合直接 # 输出格式化段落 # ,它内部调用了 # wrap函数并自动 # 拼接换行符。

2.3 shorten() — 智能截断

textwrap.shorten(text, width, placeholder=' [...]')将长文本截断至指定宽度,并在末尾追加占位符(默认为 [...])。它会在单词边界处截断,避免从中间切断单词,确保截断后的文本仍然完整可读。

import textwrap long_text = "textwrap.shorten函数用于智能截断长文本,它会自动在单词边界处截断以保证可读性。" short = textwrap.shorten(long_text, width=20) print(short) # 输出:textwrap.shorten函数用于智能截断长文本 [...] # 自定义占位符 short2 = textwrap.shorten(long_text, width=20, placeholder=" ...(更多)") print(short2) # 输出:textwrap.shorten函数用于智能截断长文本 ...(更多)

shorten()适合用于生成文章列表的摘要、搜索结果的预览片段等场景。它保证输出结果总长度(含占位符)不会超过width参数指定的宽度。

2.4 dedent() — 去除公共缩进

textwrap.dedent(text)移除文本中每行开头的公共空白前缀。这对于处理代码中的多行字符串(docstring、多行模板等)非常有用,可以去除为了保持代码缩进而引入的额外空白。

import textwrap # 多行字符串为了对齐代码引入了额外缩进 code = """ def hello(): print("Hello, World!") print("Welcome to Python!") """ # 去除公共缩进 clean = textwrap.dedent(code) print(repr(clean)) # 输出:'\ndef hello():\n print("Hello, World!")\n print("Welcome to Python!")\n' # 实际打印效果 print(clean) # def hello(): # print("Hello, World!") # print("Welcome to Python!")

dedent()会智能检测所有非空行的最小缩进量,然后将每一行的缩进统一减去该量。完全空白的行会被忽略(不影响缩进计算),制表符和空格会分别处理。

2.5 indent() — 添加统一缩进

textwrap.indent(text, prefix, predicate=None)为文本中的每一行添加指定的前缀字符串。可选参数predicate是一个回调函数,用于控制哪些行需要添加缩进(返回True的行才添加)。

import textwrap text = "第一行\n第二行\n第三行" # 为每一行添加 "> " 前缀 indented = textwrap.indent(text, "> ") print(indented) # > 第一行 # > 第二行 # > 第三行 # 使用 predicate 只为非空行添加缩进 text2 = "标题\n\n正文内容\n\n结尾" result = textwrap.indent(text2, " ", lambda line: line.strip() != "") print(result) # 标题 # # 正文内容 # # 结尾

函数对比总结

函数返回值核心用途常用场景
wrap()list[str]按宽度换行,返回行列表逐行处理、自定义拼接
fill()str按宽度换行,拼接为单个字符串直接输出格式化段落
shorten()str智能截断,追加占位符摘要预览、列表片段
dedent()str去除公共缩进docstring、模板文本清理
indent()str添加统一前缀缩进引用块、代码块格式标记

三、TextWrapper类精细控制

当五个顶层函数无法满足需求时,可以使用textwrap.TextWrapper类进行精细控制。TextWrapper是一个可配置的文本包装器,通过构造参数定义格式化规则,然后调用其wrap()fill()方法处理文本。顶层函数本质上是TextWrapper实例的快捷方式,所有参数都会透传给构造方法。

TextWrapper核心参数

参数默认值说明
width70行最大宽度,设为None则禁用换行
initial_indent''第一行的前缀字符串
subsequent_indent''后续行的前缀字符串
expand_tabsTrue是否将制表符展开为空格
tabsize8制表符对应的空格数
replace_whitespaceTrue是否将空白字符替换为空格
drop_whitespaceTrue是否去除行首尾的空白
break_long_wordsTrue是否允许在单词内换行(否则超长单词会溢出)
break_on_hyphensTrue是否在连字符处换行
fix_sentence_endingsFalse是否调整句子结尾的空格(英文专用)
max_linesNone最大行数,超出部分截断(结合placeholder)
placeholder' [...]'截断后的占位符后缀

initial_indent 与 subsequent_indent

这两个参数分别控制第一行和后续行的前缀缩进,是实现"悬挂缩进"或"引用回复"样式的关键。

import textwrap text = "这是一个演示悬挂缩进的示例文本。通过设置initial_indent和subsequent_indent参数可以实现不同的缩进样式。" wrapper = textwrap.TextWrapper( width=24, initial_indent="> ", subsequent_indent=" " ) print(wrapper.fill(text)) # > 这是一个演示悬挂 # 缩进的示例文本。 # 通过设置initial_ # indent和 # subsequent_indent # 参数可以实现不同 # 的缩进样式。

expand_tabs 与 replace_whitespace

这两个参数控制文本预处理行为。expand_tabs将所有制表符转换为空格(空格数由tabsize控制)。replace_whitespace会将所有空白字符(制表符、换行符、垂直制表符等)统一替换为普通空格,确保文本在格式化时行为一致。

import textwrap # 包含制表符的文本 text = "分类:\tPython\n版本:\t3.x\n用途:\t文本格式化" wrapper = textwrap.TextWrapper(width=30, expand_tabs=True, tabsize=4) print(wrapper.fill(text)) # 分类: Python # 版本: 3.x # 用途: 文本格式化

break_long_words 与 break_on_hyphens

当一行中出现了超长单词(如URL、文件路径、无空格长字符串)时,break_long_words=True允许在任意位置截断该单词,break_on_hyphens=True则优先在连字符处断开。对于包含URL或长标识符的文本,建议启用这两个选项以避免内容溢出列宽。

import textwrap long_url_text = "请访问我们的网站 https://www.example-very-long-domain-name.com/path/to/resource 获取更多信息" # 允许断词 wrapper1 = textwrap.TextWrapper(width=30, break_long_words=True) print(wrapper1.fill(long_url_text)) # 禁止断词(超长部分会溢出) wrapper2 = textwrap.TextWrapper(width=30, break_long_words=False) print(wrapper2.fill(long_url_text))

max_lines 与 placeholder

这是shorten()函数在TextWrapper类中的底层实现机制。通过设置max_lines限制最大行数,placeholder指定超出部分显示的占位符(默认 [...])。

import textwrap long_text = "这是一段很长的文本。它包含多个句子,需要被截断成最多三行的摘要形式。textwrap模块的TextWrapper类通过max_lines参数实现了这个功能。" wrapper = textwrap.TextWrapper(width=20, max_lines=2, placeholder=" ...") print(wrapper.fill(long_text)) # 这是一段很长的文 # 本。它包含多个句子 ...

最佳实践:对于大多数场景,优先使用顶层函数(wrap/fill/shorten/dedent/indent),代码更简洁。当需要组合多个配置参数(如同时设置首行缩进、制表符展开和最大行数)时,使用TextWrapper类更为合适。

四、dedent与indent的应用

dedentindent是一对互逆操作,分别用于去除和添加统一的缩进。在实际开发中,这两个函数常被用于代码生成、文档字符串处理和模板文本的预处理。

4.1 文档字符串的缩进去除

Python的PEP 257建议,多行文档字符串的第二行及以后应该与第一行的引号保持缩进对齐。但在实际编码中,为了保持类/函数体内的缩进层次,文档字符串往往会多出额外的缩进。dedent()正是为此而生。

def my_function(): """ 这个文档字符串前面有额外的缩进。 因为它在函数体内部,所以每行前都有8个空格。 使用dedent可以清除这些公共缩进。 """ # 手动去除缩进 import textwrap doc = textwrap.dedent(my_function.__doc__) print(doc) # 输出: # 这个文档字符串前面有额外的缩进。 # 因为它在函数体内部,所以每行前都有8个空格。 # 使用dedent可以清除这些公共缩进。

4.2 代码生成的缩进管理

当动态生成Python代码或HTML模板时,dedentindent的组合使用可以显著提升代码的可读性和维护性。

import textwrap # 模板定义(使用dedent消除对齐缩进) template = textwrap.dedent("""\

{title}

{description}

""") # 在循环中为每行添加前缀(模拟嵌套结构) for i in range(3): item = template.format( title=f"项目 {i+1}", description=f"这是第{i+1}个项目的描述" ) # 为整个块添加前缀缩进 block = textwrap.indent(item, " ") print(f"生成的HTML块 {i+1}:") print(block)

4.3 邮件回复的引用样式

在生成邮件回复或论坛回帖时,indent()配合predicate参数可以实现类似电子邮件客户端中的"引用回复"效果。

import textwrap original_msg = """用户张三: 您好,我想咨询一下关于订单号 ORD-2024-00123的物流状态。 请问预计什么时间可以送达? 谢谢!""" # 引用回复:只为非空行添加 "> " 前缀 reply_quote = textwrap.indent( original_msg, "> ", predicate=lambda line: line.strip() != "" ) reply = f"尊敬的客户,您好!\n\n以下是您的原始消息引用:\n\n{reply_quote}\n\n关于您的问题,经查询..." print(reply) # 尊敬的客户,您好! # # 以下是您的原始消息引用: # # > 用户张三: # > 您好,我想咨询一下关于订单号 # > ORD-2024-00123的物流状态。 # > 请问预计什么时间可以送达? # > 谢谢! # # 关于您的问题,经查询...

注意:dedent()去除的是公共缩进,即所有非空行共有的最小缩进量。如果某行缩进少于公共缩进,该行将保持原样(缩进不会被"减到负数")。indent()的predicate参数默认为None(所有行都添加),也可传入自定义过滤函数。

五、实战案例

以下案例展示textwrap模块在实际项目中的典型用法,涵盖了终端输出、代码排版和文本报告生成三个核心场景。

5.1 终端输出格式化 — 帮助信息排版

CLI工具的帮助信息需要适应不同的终端宽度,textwrap可以轻松实现自适应换行,避免帮助文本超出终端列宽。

import textwrap import shutil def print_help(): """打印自适应终端宽度的帮助信息。""" # 获取终端宽度 terminal_width = shutil.get_terminal_size().columns # 留出左右边距 wrap_width = min(terminal_width - 4, 72) help_text = """ textwrap-demo — 文本格式化演示工具 用法: textwrap-demo [选项] <输入文件> 选项: --width WIDTH 指定输出宽度(默认自动适应终端) --indent TEXT 设置首行缩进 --prefix TEXT 为所有行添加前缀 --no-wrap 禁用自动换行 --output FILE 将结果写入文件而非标准输出 说明: 本工具基于Python textwrap模块实现,支持自动换行、缩进控制、 制表符展开等功能。适用于需要精确控制文本排版的各类场景。 """ # 去除文档字符串的公共缩进,再按指定宽度重新填充 cleaned = textwrap.dedent(help_text).strip() formatted = textwrap.fill(cleaned, width=wrap_width) print(formatted) # 调用示例(在80列终端中输出) print_help()

5.2 代码注释排版 — 保持注释整洁

在生成代码注释或文档时,经常需要将单行长注释转换为多行注释,同时保持统一的缩进层次。

import textwrap def generate_function_doc(func_name, description, params, returns): """生成格式化的函数文档注释。""" # 初始缩进(函数体内部的4空格) base_indent = " " parts = [] # 描述段落 desc_para = textwrap.fill(description, width=68) parts.append(desc_para) # 参数说明 if params: parts.append("") parts.append("参数:") for name, desc in params: param_line = f" {name} -- {desc}" wrapped = textwrap.fill(param_line, width=68, subsequent_indent=" ") parts.append(wrapped) # 返回值说明 if returns: parts.append("") ret_line = f"返回:{returns}" parts.append(textwrap.fill(ret_line, width=68, subsequent_indent=" ")) # 组合并添加前缀缩进 doc_text = "\n".join(parts) indented = textwrap.indent(doc_text, base_indent + "# ") return f"{base_indent}\"\"\"\n{indented}\n{base_indent}\"\"\"" # 使用示例 doc = generate_function_doc( "parse_config", "解析配置文件并返回配置字典。支持JSON、YAML和INI三种格式,自动根据文件扩展名选择合适的解析器。", [("filepath", "配置文件的路径,支持绝对路径和相对路径"), ("encoding", "文件编码,默认为utf-8")], "dict — 包含所有配置项的字典对象" ) print(doc)

5.3 文本报告生成 — 格式化输出分析结果

数据分析或系统巡检脚本中,生成格式化的文本报告是一项常见需求。结合textwrap的多种函数,可以轻松打造结构清晰的信息报告。

import textwrap from datetime import datetime def generate_report(server_name, metrics, warnings, recommendations): """生成服务器巡检报告。""" now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 使用dedent编写多行模板 report = textwrap.dedent(f"""\ ======================================== 服务器巡检报告 ======================================== 服务器名称:{server_name} 生成时间:{now} ---------------------------------------- 【性能指标】 """) # 添加性能指标 for key, value in metrics.items(): line = f"{key}: {value}" report += textwrap.fill(line, width=68, initial_indent=" ", subsequent_indent=" ") + "\n" # 告警信息 if warnings: report += "\n【告警信息】\n" for w in warnings: w_wrapped = textwrap.fill(w, width=66, initial_indent=" ⚠ ", subsequent_indent=" ") report += w_wrapped + "\n" # 建议 if recommendations: report += "\n【优化建议】\n" for r in recommendations: r_wrapped = textwrap.fill(r, width=66, initial_indent=" * ", subsequent_indent=" ") report += r_wrapped + "\n" report += textwrap.dedent("""\ ======================================== 报告结束 ======================================== """) return report # 调用示例 report = generate_report( "WEB-APP-01", {"CPU使用率": "45.2%", "内存使用率": "67.8%", "磁盘使用率": "82.3%", "网络延迟": "12ms"}, ["磁盘使用率超过80%,建议及时清理历史日志文件"], ["建议配置日志自动轮转策略(logrotate),保留最近30天日志", "考虑将历史数据归档至对象存储服务以释放磁盘空间"] ) print(report)

5.4 多栏文本布局 — 模拟列式排版

利用wrap()返回列表的特性,可以实现简单的多栏文本布局,适合在邮件或终端中以列形式展示信息。

import textwrap def columnize(text, col_width=20, spacing=4): """将文本排版为双栏布局。""" lines = textwrap.wrap(text, width=col_width) return lines def print_two_column(left_text, right_text, col_width=30, spacing=4): """打印双栏对齐文本。""" left_lines = columnize(left_text, col_width) right_lines = columnize(right_text, col_width) max_lines = max(len(left_lines), len(right_lines)) # 补齐较短的列 left_lines += [''] * (max_lines - len(left_lines)) right_lines += [''] * (max_lines - len(right_lines)) for l, r in zip(left_lines, right_lines): print(f"{l:<{col_width}}{' ' * spacing}{r}") # 示例 left = "textwrap模块提供了一系列用于格式化文本段落的功能,可以控制换行位置、缩进方式和宽度。" right = "TextWrapper类允许用户通过构造参数精细控制文本排版行为,是底层核心实现。" print_two_column(left, right, col_width=24, spacing=4) # 输出大致效果(在足够宽的终端中): # textwrap模块提供了一系 TextWrapper类允许用 # 列用于格式化文本段落的 户通过构造参数精细 # 功能,可以控制换行位置 控制文本排版行为, # 、缩进方式和宽度。 是底层核心实现。

实战要点:textwrap不是万能的——它不处理中文词汇边界,不处理Markdown/HTML标签内部结构,也不生成表格对齐。但在"纯文本段落按宽度排版"这个特定领域,它是Python标准库中最直接、最成熟的解决方案。

六、核心要点总结

一、模块定位:textwrap是Python标准库中的文本排版工具,专注于"固定宽度下的文本格式化展示",不涉及文本内容分析与转换。

二、五大顶层函数:wrap()返回行列表,fill()返回拼接字符串,shorten()智能截断追加占位符,dedent()去除公共缩进,indent()添加统一前缀。五个函数覆盖了文本排版的全部核心需求。

三、TextWrapper类:通过width、initial_indent、subsequent_indent、expand_tabs、replace_whitespace、break_long_words、max_lines等参数实现精细化控制。顶层函数本质上是TextWrapper实例的快捷调用。

四、dedent与indent的对称设计:dedent消去公共缩进,适用于docstring和模板文本的清理;indent添加统一前缀,适用于引用块、代码块嵌套和邮件回复样式。二者配合可高效管理动态文本的缩进层次。

五、典型应用场景:CLI帮助信息自适应排版、代码文档字符串格式化、文本报告生成、多栏文本布局。在处理中文文本时需注意词边界问题,必要时结合中文分词工具预处理。

六、注意事项:textwrap不处理标签(HTML/XML/Markdown)内部结构,不提供表格对齐功能,不感知语义边界(如中文词汇边界)。对于这些场景,需要结合re、字符串格式化等其他工具协同使用。

学习建议:在编写任何需要输出格式化文本的Python程序时,首先考虑textwrap模块。它通常比手动实现换行逻辑更简洁、更健壮。优先使用顶层函数,需要组合配置时再使用TextWrapper类。