detailed-docx:一个能保住格式的 Word 文档操作库

项目地址:

问题

用 Python 改 Word 文档里的内容,最怕的就是格式丢失。

python-docx 是 Python 生态里操作 .docx 的标准库,功能很全,但它提供的是底层能力------你得自己处理文本碎片、格式保留、合并单元格等问题。对于"改几个字但格式不能动"这种需求,裸用 python-docx 其实不太行。

detailed-docx 就是在 python-docx 之上封装的一层,专门解决格式保留问题。

Word 的内部结构:理解 Run

要理解为什么"改个字"这么难,需要先了解 Word 文档的内部结构。

一个段落(Paragraph)并不是一整段文字,而是由多个 Run 拼接而成。每个 Run 是一段格式一致的文本片段:

复制代码
段落: "北京公司在2023年取得了非常优秀的业绩"

  Run[0]: "北京公司"     → 粗体, 蓝色, 14pt
  Run[1]: "在2023年取得了" → 常规, 黑色, 12pt
  Run[2]: "非常优秀"     → 粗体, 斜体, 红色
  Run[3]: "的业绩"       → 常规, 黑色, 12pt

每个 Run 在 XML 层面有独立的格式节点 <w:rPr>,这就是 Word 实现"一段话里混合多种格式"的机制。

问题在于:Run 的拆分是不可控的 。Word 会因为拼写检查、编辑历史、中英文切换等原因,自动把文字拆成碎片。你以为 "北京公司" 在一个 Run 里,实际上可能被拆成 "北京" 和 "公司" 两个 Run。这导致直接搜索 paragraph.text 能找到,但逐个 Run 遍历却找不到完整匹配。

如果你直接写 paragraph.text = "新内容",python-docx 会把所有 Run 清空再重建------格式全没了。

核心算法:跨 Run 映射与替换

detailed-docx 的核心是一个字符位置映射算法:

复制代码
1. 拼接所有 Run 的文本 → full_text = "北京公司在2023年取得了非常优秀的业绩"
2. 构建映射表:每个字符位置属于哪个 Run
3. 在 full_text 上搜索目标文本
4. 将匹配区间映射回各个 Run,逐个修改其 text 属性
5. 关键:只改 text,不动 <w:rPr> 格式节点

替换 "北京公司" → "上海分公司" 的实际操作:

  • Run0 的 text 从 "北京公司" 改成 "上海分公司",<w:rPr>(粗体+蓝色+14pt)完全不动
  • 如果匹配横跨多个 Run,后续 Run 只清空被命中的文字部分

这样就可以做到文本变了但是格式原封不动。

格式修改:增量叠加

修改格式同样是,保证只动你指定的,不碰其他的:

python 复制代码
# "非常优秀" 原来是 {粗体, 斜体, 红色}
editor.modify_format("非常优秀", {"underline": True})
# 结果:{粗体, 斜体, 红色, 下划线} ← 只新增,不覆盖

API 概览

python 复制代码
from my_docx import DocxEditor

# ── 读取 ──
editor = DocxEditor("input.docx")
editor.get_structural_map()        # 段落 + 表格 + 节的完整结构
editor.get_run_details(para_idx=1) # 查看某段的 Run 碎片和精确格式

# ── 替换(自动跨 Run,自动保格式)──
editor.replace_text("旧词", "新词")
editor.replace_in_table(0, 1, 3, "旧值", "新值")

# ── 格式(增量叠加)──
editor.modify_format("重点内容", {"bold": True, "color": "FF0000"})

# ── 写入空单元格 ──
editor.set_table_cell_text(0, 0, 3, "张三", {"name": "黑体", "size": 12})

# ── 创建 ──
editor = DocxEditor.create_new()
editor.add_heading("标题", level=1)
editor.add_paragraph("正文", fmt={"name": "宋体", "size": 12})

# ── 删除 ──
editor.delete_paragraph(3)
editor.delete_table_row(0, 2)

editor.save("output.docx")
相关推荐
打工仔折腾 AI31 分钟前
FaceFusion本地换脸实战:Windows整合包、模型选择与遮罩调参记录
人工智能·windows·后端·python·深度学习·性能优化·ai agent 实战
yi01132 分钟前
DAY24: LeetCode 167:两数之和 II|从暴力枚举到左右双指针
笔记·python·算法·leetcode·二分查找·双指针
kimnoic43 分钟前
Python常用命令提示符使用方法详解
开发语言·python
Crazy________44 分钟前
05Linux内存管理核心原理与运维实战
java·开发语言
Nebula_g1 小时前
JavaSE拓展:工具类Executors
java·开发语言·后端·spring·基础·javase
扶风ff1 小时前
练题簿在线免费刷题:会员线下组卷、Word 试卷与成绩导入,课堂检测更方便
java·开发语言·算法·小程序·word
程序员老陆1 小时前
C++ 多线程通信方式全景:从共享内存到结构化协调
开发语言·c++
Είναι η κοπέλα1 小时前
模型量化完全指南:GGUF、GPTQ、AWQ 怎么选
人工智能·pytorch·python
yi0111 小时前
LeetCode 15:三数之和|排序 + 双指针,如何避免重复答案?
笔记·python·算法·leetcode·双指针
weixin199701080161 小时前
《1688消息服务落地方案:alibaba.message.* 与 Webhook 回调的可靠性与重试策略》(附Python源码)
开发语言·python