段落匹配Python

wnn*_*maw 8 python

背景资料

我有一个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是一个非常有效的代码,它找到两个文本之间的差异.不幸的是,它有点太高效,所以取而代之的redundantdune结果

re dun ant e

这很丑陋,但对单个单词来说很好.但是,如果整个段落被替换,结果将完全不可读.那不行.

之前我通过将所有文本折叠成一个段落来解决这个问题,但这不太理想,因为它变得非常混乱并且仍然非常丑陋.


迄今为止的解决方案

我有一个创建修订文档的功能.这个函数传递了一个像这样设置的元组列表:

[(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段落匹配这样的东西.但如果有一种更好的方法可以避免这种完全不同的问题,那也很好.


有些事要注意:

  • 我在Windows 7 64位上运行32.6位Python 2.6.6
  • 我对我的本地副本进行了一些更改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(因为它始终与脚本位于同一目录中)

jdh*_*deb 1

尝试使用 @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(清理后)正是您正在寻找的。