背景资料
我有一个Python脚本,它使用docx模块生成word文档.这些文档基于日志生成,然后打印并存储为记录.但是,可以追溯编辑日志,因此需要修改文档记录,并且必须跟踪这些修订.我实际上并没有修改文档,而是生成一个新的文档,显示当前日志中的内容与日志中的内容之间的差异(在修改后的文件打印后更新日志).发生修订时,我的脚本使用diff_match_patch以下函数生成已更改内容的标记:
def revFinder(str1,str2):
dmp = dmp_module.diff_match_patch()
diffs = dmp.diff_main(str1,str2)
paratext = []
for diff in diffs:
paratext.append((diff[1], '' if diff[0] == 0 else ('s' if diff[0] == -1 else 'b')))
return paratext
Run Code Online (Sandbox Code Playgroud)
docx 如果需要逐字格式化,可以将文本作为字符串或元组,所以[参见"需要注意的事项"中的第二篇子句]
[("Hello, ", ''), ("my name ", 'b'), ("is Brad", 's')]
Run Code Online (Sandbox Code Playgroud)
产生
你好我的名字
是布拉德
问题
diff_match_patch是一个非常有效的代码,它找到两个文本之间的差异.不幸的是,它有点太高效,所以取而代之的redundant是dune结果
redunante
这很丑陋,但对单个单词来说很好.但是,如果整个段落被替换,结果将完全不可读.那不行.
之前我通过将所有文本折叠成一个段落来解决这个问题,但这不太理想,因为它变得非常混乱并且仍然非常丑陋.
迄今为止的解决方案
我有一个创建修订文档的功能.这个函数传递了一个像这样设置的元组列表:
[(fieldName, original, revised)]
Run Code Online (Sandbox Code Playgroud)
所以文档设置为
Orignial fieldName (With Markup)
result of revFinder diffing orignal and revised
Revised fieldName
revised
Run Code Online (Sandbox Code Playgroud)
我假设为了解决这个问题,我需要在段落之间做一些匹配,以确保我没有区分两个完全独立的段落.我也假设这种匹配将取决于是否添加或删除段落.这是我到目前为止的代码:
if len(item[1].split('\n')) + len(item[1].split('\n'))) == 2:
body.append(heading("Original {} (With Markup)".format(item[0]),2))
body.append(paragraph(revFinder(item[1],item[2])))
body.append(paragraph("",style="BodyTextKeep"))
body.append(heading("Revised {}".format(item[0]),2))
body.append(paragraph(item[2]))
body.append(paragraph(""))
else:
diff = len(item[1].split('\n')) - len(item[1].split('\n'))
if diff == 0:
body.append(heading("Original {} (With Markup)".format(item[0]),2))
for orPara, revPara in zip(item[1].split('\n'),item[2].split('\n')):
body.append(paragraph(revFinder(orPara,revPara)))
body.append(paragraph("",style="BodyTextKeep"))
body.append(heading("Revised {}".format(item[0]),2))
for para in item[2].split('\n'):
body.append(paragraph("{}".format(para)))
body.append(paragraph(""))
elif diff > 0:
#Removed paragraphs
elif diff < 0:
#Added paragraphs
Run Code Online (Sandbox Code Playgroud)
到目前为止,我已经计划使用像difflib段落匹配这样的东西.但如果有一种更好的方法可以避免这种完全不同的问题,那也很好.
有些事要注意:
docx(即通过格式化添加了删除),所以如果你测试这段代码,你将无法复制我在这方面做的事情整个过程的描述(修订步骤以粗体显示):
1)用户打开Python脚本并使用GUI将信息添加到称为"条件报告"(CR)的事物中
注意:完整的CR包含4个部分,全部由不同的人完成.但每个部分都是单独打印的.所有4个部分一起存储在日志中
2)当用户完成时,信息被保存到日志(如下所述),然后作为.docx文件打印
3)打印的文档已签名并存储
4)当用户想要修改CR的一部分时,打开GUI,并编辑每个字段中的信息.我只关心这个问题中的一些字段,那些是多行文本控件(可以产生多个段落)
5)一旦用户完成修订,代码就会生成我在"解决方案到目前为止"部分中描述的元组列表,并将其发送到生成修订文档的函数
6)修订文档的创建,打印,签名和存储与该CR的该部分的原始文档一起
7)完全重写日志以包括修订的信息
日志:
日志只是一个巨人dict,它存储了所有CR的所有信息.一般格式是
{"Unique ID Number": [list of CR info]}
Run Code Online (Sandbox Code Playgroud)
日志不存储CR的过去版本,因此在修改CR时,旧信息将被覆盖(这是我们对系统的要求).正如我之前提到的,每次编辑日志时,整个事情都会被重写.为了获取日志中的信息,我import(因为它始终与脚本位于同一目录中)
尝试使用 @tzaman 上面提到的 diff_match_patch 的差异后清理选项,特别是检查diff_cleanupSemantic当差异输出旨在供人类可读时使用的函数。
清理选项不会自动运行,因为 diff_match_patch 提供了多个清理选项供您选择(根据您的需要)。
这是一个例子:
import diff_match_patch
dmp = diff_match_patch.diff_match_patch()
diffs = dmp.diff_main('This is my original paragraph.', 'My paragraph is much better now.')
print diffs # pre-cleanup
dmp.diff_cleanupSemantic(diffs)
print diffs # post cleanup
Run Code Online (Sandbox Code Playgroud)
输出:
[(-1, 'This is m'), (1, 'M'), (0, 'y'), (-1, ' original'), (0, ' paragraph'), (1, ' is much better now'), (0, '.')]
[(-1, 'This is my original paragraph'), (1, 'My paragraph is much better now'), (0, '.')]
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,第一个 diff 是最佳的,但不可读,而第二个 diff(清理后)正是您正在寻找的。