首先感谢 艾派森 老师的 Python操作word基础 。
感谢 GIS开发者 老师的 使用python-docx实现对word文档里的字符串、图片批量替换
我曾将尝试将整个段落paragraph的runs合并成1个run,再进行替换,方案可行。也可以清除runs的内容,将最后一个run替换成修改后的数据,方案可行。不过缺点是同一个段落的样式都会按照最后一个文字块run的样式来,为了保留同一个段落的不同样式,我学习了 GIS开发者 老师的 使用python-docx实现对word文档里的字符串、图片批量替换 ,解决了样式问题,但出现了匹配问题,比如这种情况 “{2}月{ ”,会导致后一个匹配项匹配不到。
我根据我的理解进行了优化。
模板如下:
替换后的新文档如下:
代码如下:
# pip install python-docx
from docx import Document
import re
def write(path_template, path_new, dic_old_new, start_mark='{', end_mark='}'):
# 将所有需要替换的数据存入一个集合,备用
olds = set(dic_old_new.keys())
# 打开文档
doc1 = Document(path_template)
# 读取里面的数据
for p in doc1.paragraphs:
# 检测本段是否有需要替换的数据
olds_temp = set()
for old in olds:
if old in p.text:
olds_temp.add(old)
if len(olds_temp) == 0:
continue
# 检测本段的run,将包含old的run拼接,替换。
merge_mark = False
tmp = ''
runs = p.runs
for i, run in enumerate(runs):
t = run.text
# 开始标志'{'如果在本run中,启动拼接
if start_mark in t:
merge_mark = True
# 拼接run内容,同时清除当前run内容,直到发现结束标志'}'
if merge_mark is True:
tmp += t
run.text = ''
# 结束标志'}'如果在本run中,结束拼接,开始替换
if end_mark in t:
merge_mark = False
# 提取tmp需替换的部分
lis_old_this = re.findall(f'{start_mark}.+?{end_mark}', tmp)
# 如果出现{2}月{ 情况,则将{之后的部分提取,放入下一个tmp中,同时merge_mark = True。
lis_start_mark = re.findall(start_mark, tmp)
if len(lis_old_this) < len(lis_start_mark):
tmp_end = start_mark + tmp.split(start_mark)[-1]
tmp2 = tmp[:-len(tmp_end)]
tmp = tmp_end
merge_mark = True
else:
tmp2 = tmp
tmp = ''
# 替换
for old_this in lis_old_this:
if old_this in olds_temp:
new = dic_old_new[old_this]
tmp2 = tmp2.replace(old_this, new)
# 将目前的run内容替换成tep2
run.text = run.text.replace(run.text, tmp2)
# 保存新文件
doc1.save(path_new)
if __name__ == '__main__':
dic_old_new = {'{0}': '00001', '{1}': '2030', '{2}': '01', '{3}': '01',
'{4}': '01', '{5}': '01', '{6}': '闯红灯', '{7}': '600'}
write('template.docx', 'new.docx', dic_old_new)