当前位置: 首页 > news >正文

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

项目地址:

  • GitHub: Andrew82106/GroundSkills/detailed-docx →my_skills/detailed-docx/
  • 依赖:pip install python-docx

问题

用 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> 格式节点

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

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

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

格式修改:增量叠加

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

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

API 概览

frommy_docximportDocxEditor# ── 读取 ──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")
http://www.jsqmd.com/news/555578/

相关文章:

  • Blackbox跨环境部署与版本迁移完全指南:从问题排查到落地实践
  • 20254202 2025-2026-2 《Python程序设计》实验1报告
  • Kotlin的Socket连接与UDP广播和接收
  • 跨平台虚拟机自动化控制:从繁琐操作到一键部署的效率革命
  • 终极指南:3分钟掌握QMK Toolbox键盘固件刷写技巧
  • 为什么92%的工业网关Python配置在上线72小时内崩溃?——基于37个真实产线案例的配置健壮性白皮书
  • Mac Mouse Fix技术架构深度解析:从Objective-C到Swift的混合架构演进
  • MQTT遗嘱消息实战:如何用LWT+保留消息打造智能家居设备离线预警系统
  • WhisperLive:高性能实时语音转文本架构解析与多引擎优化方案
  • 3种PostHog部署模式:为不同规模团队定制的数据分析平台搭建指南
  • 华三M-LAG实战:从零构建高可用数据中心网络
  • OpenClaw异常处理大全:nanobot任务失败自救指南
  • Agnet
  • foobox-cn:让foobar2000音乐播放体验提升300%的开源界面增强工具
  • springboot-vue基于web的小说在线阅读平台
  • springboot-vue基于web的智慧党建平台设计与实现
  • 微信小游戏过审实战:JS混淆与马甲包规避技巧
  • Pixel Dream Workshop参数详解:CFG/Steps/Scale三维度精准控制像素粒度
  • 3个技巧让LibreTranslate翻译模型部署速度提升80%
  • 中西医结合内科主治备考,找对机构才靠谱 - 医考机构品牌测评专家
  • 模拟IC设计中的‘效率’权衡:深入理解gm/ID如何平衡增益、带宽与噪声
  • 别只当摆设!深度挖掘Kylin V10 SP1安全中心的‘应用保护’与‘设备安全’实战用法
  • 【架构实战】数据备份与灾难恢复策略
  • 别只测正常工况了!用CAPL给ECU做‘压力测试’:模拟总线错误全场景复盘
  • Django+MySQL遇到emoji报错?5分钟搞定utf8mb4字符集配置
  • 别再让用户下载乱码文件了!华为云OBS临时链接重命名实战(Java版)
  • 别再死记硬背命令了!用eNSP模拟器搞懂三层交换的‘一次路由,多次交换’
  • 实测!新疆护栏定制工厂哪家靠谱?新疆昆仑宏博护栏厂 本地自营 按需定制 全方位测评(市政/小区/工地适用) - 宁夏壹山网络
  • OpenClaw技能开发入门:基于nanobot定制个人自动化模块
  • 计算机毕业设计springboot盐城市亭湖区药店销售管理系统 基于SpringBoot的盐城亭湖区医药零售信息化管理平台 亭湖区智慧药店进销存与在线服务系统