Python difflib:如何在线高亮显示差异?

34 投票
3 回答
18896 浏览
提问于 2025-04-15 11:12

在比较相似的行时,我想在同一行上突出显示不同之处:

a) lorem ipsum dolor sit amet
b) lorem foo ipsum dolor amet

lorem <ins>foo</ins> ipsum dolor <del>sit</del> amet

虽然 difflib.HtmlDiff 看起来可以做到这种行内高亮,但它生成的标记代码非常冗长。

不幸的是,我没有找到其他的类或方法,它们不是逐行操作的。

我是不是漏掉了什么?

任何建议都非常感谢!

3 个回答

3

difflib.SequenceMatcher 是一个可以处理单行文本的工具。你可以利用它提供的“操作码”来找出如何修改第一行,使它变成第二行。

9

这里有一个内联的差异比较工具,灵感来自@tzot的上面的回答(也兼容Python 3):

def inline_diff(a, b):
    import difflib
    matcher = difflib.SequenceMatcher(None, a, b)
    def process_tag(tag, i1, i2, j1, j2):
        if tag == 'replace':
            return '{' + matcher.a[i1:i2] + ' -> ' + matcher.b[j1:j2] + '}'
        if tag == 'delete':
            return '{- ' + matcher.a[i1:i2] + '}'
        if tag == 'equal':
            return matcher.a[i1:i2]
        if tag == 'insert':
            return '{+ ' + matcher.b[j1:j2] + '}'
        assert False, "Unknown tag %r"%tag
    return ''.join(process_tag(*t) for t in matcher.get_opcodes())

这个工具并不是完美的,比如说,它可以改进'替换'操作,让它能够识别被替换的完整单词,而不仅仅是几个不同的字母,但这已经是一个很好的起点了。

示例输出:

>>> a='Lorem ipsum dolor sit amet consectetur adipiscing'
>>> b='Lorem bananas ipsum cabbage sit amet adipiscing'
>>> print(inline_diff(a, b))
Lorem{+  bananas} ipsum {dolor -> cabbage} sit amet{-  consectetur} adipiscing
54

对于你的简单例子:

import difflib
def show_diff(seqm):
    """Unify operations between two compared strings
seqm is a difflib.SequenceMatcher instance whose a & b are strings"""
    output= []
    for opcode, a0, a1, b0, b1 in seqm.get_opcodes():
        if opcode == 'equal':
            output.append(seqm.a[a0:a1])
        elif opcode == 'insert':
            output.append("<ins>" + seqm.b[b0:b1] + "</ins>")
        elif opcode == 'delete':
            output.append("<del>" + seqm.a[a0:a1] + "</del>")
        elif opcode == 'replace':
            raise NotImplementedError("what to do with 'replace' opcode?")
        else:
            raise RuntimeError("unexpected opcode")
    return ''.join(output)

>>> sm= difflib.SequenceMatcher(None, "lorem ipsum dolor sit amet", "lorem foo ipsum dolor amet")
>>> show_diff(sm)
'lorem<ins> foo</ins> ipsum dolor <del>sit </del>amet'

这个是可以用在字符串上的。你需要决定一下“替换”操作该怎么处理。

撰写回答