Python difflib: выделение различий в строке? - PullRequest
25 голосов
/ 21 апреля 2009

При сравнении похожих строк я хочу выделить различия в одной строке:

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 ]

42 голосов
/ 25 апреля 2009

Для вашего простого примера:

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'

Это работает со строками. Вы должны решить, что делать с «заменой» кодов операций.

2 голосов
/ 22 апреля 2009

difflib.SequenceMatcher будет работать в одной строке. Вы можете использовать «коды операций», чтобы определить, как изменить первую строку, чтобы сделать ее второй.

1 голос
/ 03 декабря 2017

Вот встроенное отличие, вдохновленное ответом @ 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
...