difflib — Помощники для вычисления дельт

Исходный код: Lib/difflib.py


Этот модуль предоставляет классы и функции для сравнения последовательностей. Его можно использовать, например, для сравнения файлов, и он может генерировать информацию о различиях в файлах в различных форматах, включая HTML, контекст и унифицированные различия. Для сравнения каталогов и файлов смотрите также модуль filecmp.

class difflib.SequenceMatcher

Это гибкий класс для сравнения пар последовательностей любого типа, при условии, что элементы последовательности равны hashable. Базовый алгоритм предшествует алгоритму, опубликованному в конце 1980-х годов Рэтклиффом и Обершелпом под гиперболическим названием «сопоставление гештальт-паттернов», и немного более причудлив по сравнению с ним. Идея состоит в том, чтобы найти самую длинную непрерывную совпадающую подпоследовательность, которая не содержит «ненужных» элементов; эти «ненужные» элементы в некотором смысле неинтересны, например, пустые строки или пробелы. (Обработка нежелательной информации является расширением алгоритма Ратклиффа и Обершелпа.) Затем та же идея рекурсивно применяется к фрагментам последовательностей слева и справа от соответствующей подпоследовательности. Это не приводит к минимальным последовательностям редактирования, но, как правило, приводит к совпадениям, которые «выглядят правильно» для людей.

** Хронометраж:** Основной алгоритм Ратклиффа-Обершелпа использует кубическое время в наихудшем случае и квадратичное время в ожидаемом случае. SequenceMatcher - квадратичное время в наихудшем случае, и его поведение в ожидаемом случае сложным образом зависит от того, сколько элементов имеют общие последовательности.; в лучшем случае время является линейным.

Автоматическая эвристика нежелательной информации: SequenceMatcher поддерживает эвристику, которая автоматически рассматривает определенные элементы последовательности как нежелательные. Эвристика подсчитывает, сколько раз каждый отдельный элемент появляется в последовательности. Если количество дубликатов товара (после первого) составляет более 1% от всей последовательности и длина последовательности составляет не менее 200 товаров, этот товар помечается как «популярный» и рассматривается как нежелательный для целей сопоставления последовательности. Эту эвристику можно отключить, установив для аргумента autojunk значение False при создании SequenceMatcher.

Изменено в версии 3.2: Добавлен параметр автозапуск.

class difflib.Differ

Это класс для сравнения последовательностей строк текста и создания удобочитаемых различий или дельт. Different использует SequenceMatcher как для сравнения последовательностей строк, так и для сравнения последовательностей символов в похожих (почти совпадающих) строках.

Каждая строка дельты Differ начинается с двухбуквенного кода:

Код

Значение

'- '

строка, уникальная для последовательности 1

'+ '

строка, уникальная для последовательности 2

'  '

линия, общая для обеих последовательностей

'? '

строка, отсутствующая ни в одной из входных последовательностей

Строки, начинающиеся с «?», указывают на внутренние различия, которых не было ни в одной из введенных последовательностей. Эти строки могут сбивать с толку, если последовательности содержат символы табуляции.

class difflib.HtmlDiff

Этот класс может быть использован для создания HTML-таблицы (или полного HTML-файла, содержащего таблицу), показывающей параллельное, построчное сравнение текста с выделением изменений между строками и внутри строки. Таблица может быть создана как в режиме полной, так и в режиме контекстных различий.

Конструктором для этого класса является:

__init__(tabsize=8, wrapcolumn=None, linejunk=None, charjunk=IS_CHARACTER_JUNK)

Инициализирует экземпляр HtmlDiff.

tabsize является необязательным аргументом ключевого слова для указания интервала между концами табуляции и по умолчанию равен 8.

wrapcolumn - необязательное ключевое слово для указания номера столбца, в котором строки прерываются и переносятся, по умолчанию используется None, в котором строки не переносятся.

linejunk и charjunk являются необязательными ключевыми аргументами, передаваемыми в ndiff() (используются HtmlDiff для создания параллельных HTML-различий). Значения и описания аргументов по умолчанию приведены в документации ndiff().

Следующие методы являются общедоступными:

make_file(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5, *, charset='utf-8')

Сравнивает fromlines и tolines (списки строк) и возвращает строку, которая представляет собой полный HTML-файл, содержащий таблицу, показывающую построчные различия с выделенными изменениями между строками и внутри строки.

fromdesc и todesc являются необязательными аргументами ключевых слов для указания строк заголовка столбца from/to файла (по умолчанию используется пустая строка).

context и numlines являются необязательными аргументами ключевого слова. Установите для параметра context значение True, если необходимо отобразить контекстные различия, в противном случае по умолчанию используется значение False для отображения полных файлов. numlines по умолчанию имеет значение 5. Когда значение context равно True, numlines управляет количеством контекстных строк, которые окружают выделенные различия. Когда значение context равно False numlines определяет количество строк, которые отображаются перед выделением различий при использовании гиперссылок «next» (значение, равное нулю, приведет к тому, что гиперссылки «next» разместят следующую выделенную разницу в верхней части браузера без каких-либо начальных строк). контекст).

Примечание

fromdesc и todesc интерпретируются как неэкранированный HTML и должны быть должным образом экранированы при получении входных данных из ненадежных источников.

Изменено в версии 3.5: кодировка Добавлен аргумент, содержащий только ключевое слово. Кодировка HTML-документа по умолчанию изменена с 'ISO-8859-1' на 'utf-8'.

make_table(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5)

Сравнивает fromlines и tolines (списки строк) и возвращает строку, которая представляет собой полную HTML-таблицу, показывающую построчные различия с выделенными изменениями между строками и внутри строки.

Аргументы для этого метода те же, что и для метода make_file().

Tools/scripts/diff.py является интерфейсом командной строки для этого класса и содержит хороший пример его использования.

difflib.context_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')

Сравните a и b (списки строк); верните дельту (a generator, генерирующую дельта-строки) в формате контекстной разницы.

Контекстные различия - это компактный способ отображения только тех строк, которые были изменены, плюс несколько строк контекста. Изменения отображаются в виде «до» и «после». Количество контекстных строк задается значением n, которое по умолчанию равно трем.

По умолчанию управляющие строки diff (те, что содержат *** или ---) создаются с завершающей новой строкой. Это полезно для того, чтобы входные данные, созданные из io.IOBase.readlines(), приводили к различиям, которые подходят для использования с io.IOBase.writelines(), поскольку как входные, так и выходные данные имеют завершающие новые строки.

Для входных данных, которые не содержат завершающих символов новой строки, установите для аргумента lineterm значение "", чтобы выходные данные были равномерно свободны от новой строки.

Формат контекстных различий обычно содержит заголовок для имен файлов и времени изменения. Любой из них или все они могут быть указаны с помощью строк для fromfile, tofile, fromfiledate и tofiledate. Время внесения изменений обычно указывается в формате ISO 8601. Если это не указано, то строки по умолчанию будут пустыми.

>>> import sys
>>> from difflib import *
>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n']
>>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n']
>>> sys.stdout.writelines(context_diff(s1, s2, fromfile='before.py',
...                        tofile='after.py'))
*** before.py
--- after.py
***************
*** 1,4 ****
! bacon
! eggs
! ham
  guido
--- 1,4 ----
! python
! eggy
! hamster
  guido

Более подробный пример приведен в разделе Интерфейс командной строки для difflib.

difflib.get_close_matches(word, possibilities, n=3, cutoff=0.6)

Возвращает список наилучших, «достаточно хороших» совпадений. word - это последовательность, для которой требуются близкие совпадения (обычно это строка), а possibilities - это список последовательностей, с которыми требуется сопоставить word (обычно это список строк).

Необязательный аргумент n (по умолчанию 3) - это максимальное количество возвращаемых совпадений; n должно быть больше, чем 0.

Необязательный аргумент cutoff (по умолчанию 0.6) - это значение с плавающей запятой в диапазоне [0, 1]. Варианты, которые не соответствуют параметру word, игнорируются.

Наилучшие (не более n) совпадения среди возможных вариантов отображаются в виде списка, отсортированного по степени сходства, сначала выбирается наиболее похожий.

>>> get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy'])
['apple', 'ape']
>>> import keyword
>>> get_close_matches('wheel', keyword.kwlist)
['while']
>>> get_close_matches('pineapple', keyword.kwlist)
[]
>>> get_close_matches('accept', keyword.kwlist)
['except']
difflib.ndiff(a, b, linejunk=None, charjunk=IS_CHARACTER_JUNK)

Сравните a и b (списки строк); верните дельту в стиле Differ (a generator генерирует дельта-строки).

Необязательные параметры ключевых слов linejunk и charjunk являются функциями фильтрации (или None):

linejunk: Функция, которая принимает единственный строковый аргумент и возвращает значение true, если строка является ненужной, или значение false, если нет. Значение по умолчанию None. Существует также функция на уровне модуля IS_LINE_JUNK(), которая отфильтровывает строки без видимых символов, за исключением не более одного символа-фунта ('#') – однако базовый класс SequenceMatcher выполняет динамический анализ того, какие строки являются настолько часто, что возникает шум, и это обычно работает лучше, чем при использовании данной функции.

charjunk: функция, которая принимает символ (строку длиной 1) и возвращает, если символ является ненужным, или false, если нет. По умолчанию используется функция на уровне модуля IS_CHARACTER_JUNK(), которая отфильтровывает пробельные символы (пробел или табуляция; включать в нее новую строку - плохая идея!).

Tools/scripts/ndiff.py - это интерфейс командной строки для этой функции.

>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True),
...              'ore\ntree\nemu\n'.splitlines(keepends=True))
>>> print(''.join(diff), end="")
- one
?  ^
+ ore
?  ^
- two
- three
?  -
+ tree
+ emu
difflib.restore(sequence, which)

Возвращает одну из двух последовательностей, которые сгенерировали дельту.

Учитывая последовательность , полученную с помощью :meth:`Differ.compare` или :func:`ndiff`, извлеките строки, исходящие из файла 1 или 2 (параметр *which), удалив префиксы строк.

Пример:

>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True),
...              'ore\ntree\nemu\n'.splitlines(keepends=True))
>>> diff = list(diff) # materialize the generated delta into a list
>>> print(''.join(restore(diff, 1)), end="")
one
two
three
>>> print(''.join(restore(diff, 2)), end="")
ore
tree
emu
difflib.unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')

Сравните a и b (списки строк); верните дельту (a generator, генерирующую дельта-строки) в унифицированном формате diff.

Унифицированные различия - это компактный способ отображения только тех строк, которые были изменены, плюс несколько строк контекста. Изменения отображаются в виде встроенного текста (вместо отдельных блоков «до» и «после»). Количество строк контекста задается значением n, которое по умолчанию равно трем.

По умолчанию управляющие строки diff (те, что содержат ---, +++, или @@) создаются с завершающей новой строкой. Это полезно для того, чтобы входные данные, созданные из io.IOBase.readlines(), приводили к различиям, которые подходят для использования с io.IOBase.writelines(), поскольку как входные, так и выходные данные имеют завершающие новые строки.

Для входных данных, которые не содержат завершающих символов новой строки, установите для аргумента lineterm значение "", чтобы выходные данные были равномерно свободны от новой строки.

Унифицированный формат diff обычно содержит заголовок для имен файлов и времени изменения. Любой из них или все они могут быть указаны с помощью строк для fromfile, tofile, fromfiledate и tofiledate. Время внесения изменений обычно указывается в формате ISO 8601. Если это не указано, то строки по умолчанию будут пустыми.

>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n']
>>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n']
>>> sys.stdout.writelines(unified_diff(s1, s2, fromfile='before.py', tofile='after.py'))
--- before.py
+++ after.py
@@ -1,4 +1,4 @@
-bacon
-eggs
-ham
+python
+eggy
+hamster
 guido

Более подробный пример приведен в разделе Интерфейс командной строки для difflib.

difflib.diff_bytes(dfunc, a, b, fromfile=b'', tofile=b'', fromfiledate=b'', tofiledate=b'', n=3, lineterm=b'\n')

Сравните a и b (списки объектов bytes), используя dfunc; получите последовательность дельта-строк (также байтов) в формате, возвращаемом dfunc. dfunc должен быть вызываемым, обычно либо unified_diff(), либо context_diff().

Позволяет сравнивать данные с неизвестной или несогласованной кодировкой. Все входные данные, кроме n, должны быть объектами bytes, а не str. Работает путем преобразования всех входных данных (кроме n) в str без потерь и вызова dfunc(a, b, fromfile, tofile, fromfiledate, tofiledate, n, lineterm). Выходные данные dfunc затем преобразуются обратно в байты, поэтому получаемые вами дельта-строки имеют те же неизвестные/несогласованные кодировки, что и a и b.

Добавлено в версии 3.5.

difflib.IS_LINE_JUNK(line)

Возвращает True для игнорируемых строк. Строка line игнорируется, если line пуста или содержит один '#', в противном случае она не игнорируется. Используется по умолчанию для параметра linejunk в ndiff() в более старых версиях.

difflib.IS_CHARACTER_JUNK(ch)

Возвращает True для игнорируемых символов. Символ ch игнорируется, если ch является пробелом или символом табуляции, в противном случае он не игнорируется. Используется по умолчанию для параметра charjunk в ndiff().

См.также

Pattern Matching: The Gestalt Approach

Обсуждение аналогичного алгоритма Джоном У. Рэтклиффом и Д. Э. Метцнером. Это было опубликовано в Dr. Dobb’s Journal в июле 1988 года.

Объекты SequenceMatcher

В классе SequenceMatcher есть этот конструктор:

class difflib.SequenceMatcher(isjunk=None, a='', b='', autojunk=True)

Необязательный аргумент isjunk должен быть None (по умолчанию) или функция с одним аргументом, которая принимает элемент последовательности и возвращает значение true тогда и только тогда, когда элемент является «ненужным» и должен игнорироваться. Передача None для isjunk эквивалентна передаче lambda x: False; другими словами, никакие элементы не игнорируются. Например, передача:

lambda x: x in " \t"

если вы сравниваете строки как последовательности символов и не хотите синхронизировать их с пробелами или жесткими табуляциями.

Необязательные аргументы a и b являются сравниваемыми последовательностями; по умолчанию используются пустые строки. Элементы обеих последовательностей должны быть hashable.

Необязательный аргумент autojunk можно использовать для отключения автоматической эвристики нежелательной почты.

Изменено в версии 3.2: Добавлен параметр автозапуск.

Объекты SequenceMatcher получают три атрибута данных: bjunk - это набор элементов из b, для которых isjunk равен True; bpopular - это набор не мусорных элементов, которые эвристика считает популярными (если она не отключена); b2j это dict, отображающий остальные элементы b в список позиций, в которых они встречаются. Все три параметра сбрасываются всякий раз, когда b сбрасывается с помощью set_seqs() или set_seq2().

Добавлено в версии 3.2: Атрибуты bjunk и bpopular.

SequenceMatcher объекты имеют следующие методы:

set_seqs(a, b)

Установите две последовательности для сравнения.

SequenceMatcher вычисляет и кэширует подробную информацию о второй последовательности, поэтому, если вы хотите сравнить одну последовательность со многими последовательностями, используйте set_seq2() для однократного задания часто используемой последовательности и повторно вызывайте set_seq1(), по одному разу для каждой из следующих последовательностей. другие последовательности.

set_seq1(a)

Установите первую последовательность для сравнения. Вторая последовательность для сравнения не изменяется.

set_seq2(b)

Установите вторую последовательность для сравнения. Первая последовательность для сравнения не изменяется.

find_longest_match(alo=0, ahi=None, blo=0, bhi=None)

Найдите самый длинный совпадающий блок в a[alo:ahi] и b[blo:bhi].

Если значение isjunk было опущено или None, find_longest_match() возвращает (i, j, k) таким образом, что a[i:i+k] равно b[j:j+k], где alo <= i <= i+k <= ahi и blo <= j <= j+k <= bhi. Для всех (i', j', k'), удовлетворяющих этим условиям, также выполняются дополнительные условия k >= k', i <= i', и если i == i', j <= j'. Другими словами, из всех максимально совпадающих блоков верните тот, который начинается раньше всего в a, а из всех максимально совпадающих блоков, которые начинаются раньше всего в a, верните тот, который начинается раньше всего в b.

>>> s = SequenceMatcher(None, " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
Match(a=0, b=4, size=5)

Если было указано значение isjunk, сначала определяется самый длинный соответствующий блок, как указано выше, но с дополнительным ограничением, чтобы в блоке не было ненужных элементов. Затем этот блок расширяется, насколько это возможно, путем сопоставления (только) ненужных элементов с обеих сторон. Таким образом, результирующий блок никогда не совпадает с мусором, за исключением случаев, когда идентичный мусор оказывается рядом с интересным совпадением.

Вот тот же пример, что и раньше, но с учетом того, что пробелы считаются ненужными. Это не позволяет ' abcd' напрямую соответствовать ' abcd' в конце второй последовательности. Вместо этого может совпадать только 'abcd', который совпадает с крайним левым 'abcd' во второй последовательности:

>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
Match(a=1, b=0, size=4)

Если ни один из блоков не совпадает, то возвращается (alo, blo, 0).

Этот метод возвращает named tuple Match(a, b, size).

Изменено в версии 3.9: Добавлены аргументы по умолчанию.

get_matching_blocks()

Возвращает список троек, описывающих непересекающиеся совпадающие подпоследовательности. Каждая тройка имеет вид (i, j, n) и означает, что a[i:i+n] == b[j:j+n]. Тройки монотонно увеличиваются в i и j.

Последняя тройка является фиктивной и имеет значение (len(a), len(b), 0). Это единственная тройка с n == 0. Если (i, j, n) и (i', j', n') являются соседними тройками в списке, а вторая тройка не является последней в списке, то i+n < i' или j+n < j'; другими словами, соседние тройки всегда описывают несмежные равные блоки.

>>> s = SequenceMatcher(None, "abxcd", "abcd")
>>> s.get_matching_blocks()
[Match(a=0, b=0, size=2), Match(a=3, b=2, size=2), Match(a=5, b=4, size=0)]
get_opcodes()

Возвращает список из 5 кортежей, описывающих, как превратить a в b. Каждый кортеж имеет вид (tag, i1, i2, j1, j2). Первый кортеж имеет значение i1 == j1 == 0, а остальные кортежи имеют значение i1, равное i2 из предыдущего кортежа, и, аналогично, j1, равное предыдущему j2.

Значения tag представляют собой строки со следующими значениями:

Ценность

Значение

'replace'

a[i1:i2] следует заменить на b[j1:j2].

'delete'

a[i1:i2] должно быть удалено. Обратите внимание, что в этом случае j1 == j2.

'insert'

b[j1:j2] должно быть вставлено в a[i1:i1]. Обратите внимание, что в этом случае i1 == i2.

'equal'

a[i1:i2] == b[j1:j2] (подпоследовательности равны).

Например:

>>> a = "qabxcd"
>>> b = "abycdf"
>>> s = SequenceMatcher(None, a, b)
>>> for tag, i1, i2, j1, j2 in s.get_opcodes():
...     print('{:7}   a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format(
...         tag, i1, i2, j1, j2, a[i1:i2], b[j1:j2]))
delete    a[0:1] --> b[0:0]      'q' --> ''
equal     a[1:3] --> b[0:2]     'ab' --> 'ab'
replace   a[3:4] --> b[2:3]      'x' --> 'y'
equal     a[4:6] --> b[3:5]     'cd' --> 'cd'
insert    a[6:6] --> b[5:6]       '' --> 'f'
get_grouped_opcodes(n=3)

Возвращает generator групп, содержащих до n строк контекста.

Начиная с групп, возвращаемых с помощью get_opcodes(), этот метод разбивает меньшие кластеры изменений и устраняет промежуточные диапазоны, в которых изменений нет.

Группы возвращаются в том же формате, что и get_opcodes().

ratio()

Возвращает меру сходства последовательностей в виде числа с плавающей точкой в диапазоне [0, 1].

Где T - общее количество элементов в обеих последовательностях, а M - количество совпадений, это 2,0*M / T. Обратите внимание, что это 1.0, если последовательности идентичны, и 0.0, если у них нет ничего общего.

Это дорогостоящее вычисление, если get_matching_blocks() или get_opcodes() еще не были вызваны, и в этом случае вы можете сначала попробовать quick_ratio() или real_quick_ratio(), чтобы получить верхнюю границу.

Примечание

Внимание: Результат вызова ratio() может зависеть от порядка аргументов. Например:

>>> SequenceMatcher(None, 'tide', 'diet').ratio()
0.25
>>> SequenceMatcher(None, 'diet', 'tide').ratio()
0.5
quick_ratio()

Относительно быстро возвращает верхнюю границу для ratio().

real_quick_ratio()

Верните верхнюю границу для ratio() очень быстро.

Три метода, которые возвращают отношение совпадения к общему количеству символов, могут давать разные результаты из-за различных уровней аппроксимации, хотя quick_ratio() и real_quick_ratio() всегда по меньшей мере равны ratio():

>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.quick_ratio()
0.75
>>> s.real_quick_ratio()
1.0

Примеры последовательностей

В этом примере сравниваются две строки, в которых пробелы считаются «ненужными».:

>>> s = SequenceMatcher(lambda x: x == " ",
...                     "private Thread currentThread;",
...                     "private volatile Thread currentThread;")

ratio() возвращает значение с плавающей точкой в [0, 1], измеряющее сходство последовательностей. Как правило, значение ratio() больше 0,6 означает, что последовательности почти совпадают:

>>> print(round(s.ratio(), 3))
0.866

Если вас интересует только совпадение последовательностей, get_matching_blocks() будет удобно:

>>> for block in s.get_matching_blocks():
...     print("a[%d] and b[%d] match for %d elements" % block)
a[0] and b[0] match for 8 elements
a[8] and b[17] match for 21 elements
a[29] and b[38] match for 0 elements

Обратите внимание, что последний кортеж, возвращаемый get_matching_blocks(), всегда является фиктивным, (len(a), len(b), 0), и это единственный случай, когда последний элемент кортежа (количество совпадающих элементов) равен 0.

Если вы хотите знать, как изменить первую последовательность на вторую, используйте get_opcodes():

>>> for opcode in s.get_opcodes():
...     print("%6s a[%d:%d] b[%d:%d]" % opcode)
 equal a[0:8] b[0:8]
insert a[8:8] b[8:17]
 equal a[8:29] b[17:38]

См.также

  • Функция get_close_matches() в этом модуле, которая показывает, как простой код, основанный на SequenceMatcher, может быть использован для выполнения полезной работы.

  • Simple version control recipe для небольшого приложения, созданного с помощью SequenceMatcher.

Разные объекты

Обратите внимание, что Differ-сгенерированные значения delta не претендуют на то, чтобы быть минимальными различиями. Напротив, минимальные различия часто противоречат интуиции, потому что они синхронизируются везде, где это возможно, иногда случайно совпадают с интервалом в 100 страниц. Ограничение точек синхронизации смежными совпадениями сохраняет некоторое представление о локальности, но иногда приводит к увеличению размера различий.

В классе Differ есть этот конструктор:

class difflib.Differ(linejunk=None, charjunk=None)

Необязательные параметры ключевых слов linejunk и charjunk предназначены для функций фильтрации (или None):

linejunk: Функция, которая принимает единственный строковый аргумент и возвращает значение true, если строка является ненужной. Значение по умолчанию None, что означает, что ни одна строка не считается ненужной.

charjunk: функция, которая принимает односимвольный аргумент (строку длиной 1) и возвращает значение true, если символ является ненужным. Значение по умолчанию None, что означает, что ни один символ не считается ненужным.

Эти функции фильтрации нежелательной почты ускоряют поиск различий и не приводят к игнорированию каких-либо отличающихся строк или символов. Для получения более подробной информации ознакомьтесь с описанием параметра isjunk для метода find_longest_match().

Differ объекты используются (генерируются дельты) с помощью одного метода:

compare(a, b)

Сравните две последовательности линий и сгенерируйте дельту (последовательность линий).

Каждая последовательность должна содержать отдельные однострочные строки, заканчивающиеся символами новой строки. Такие последовательности могут быть получены с помощью метода readlines() для файлоподобных объектов. Сгенерированная разность также состоит из строк, заканчивающихся новой строкой, готовых к печати как есть с помощью метода writelines() объекта, подобного файлу.

Другой пример

В этом примере сравниваются два текста. Сначала мы настраиваем тексты - последовательности отдельных однострочных строк, заканчивающихся символами новой строки (такие последовательности также можно получить с помощью метода readlines() для файлоподобных объектов).:

>>> text1 = '''  1. Beautiful is better than ugly.
...   2. Explicit is better than implicit.
...   3. Simple is better than complex.
...   4. Complex is better than complicated.
... '''.splitlines(keepends=True)
>>> len(text1)
4
>>> text1[0][-1]
'\n'
>>> text2 = '''  1. Beautiful is better than ugly.
...   3.   Simple is better than complex.
...   4. Complicated is better than complex.
...   5. Flat is better than nested.
... '''.splitlines(keepends=True)

Затем мы создаем экземпляр объекта Different:

>>> d = Differ()

Обратите внимание, что при создании экземпляра объекта Differ мы можем передавать функции для фильтрации «ненужных» строк и символов. Подробности смотрите в конструкторе Differ().

Наконец, мы сравниваем эти два:

>>> result = list(d.compare(text1, text2))

result - это список строк, так что давайте оформим его красиво:

>>> from pprint import pprint
>>> pprint(result)
['    1. Beautiful is better than ugly.\n',
 '-   2. Explicit is better than implicit.\n',
 '-   3. Simple is better than complex.\n',
 '+   3.   Simple is better than complex.\n',
 '?     ++\n',
 '-   4. Complex is better than complicated.\n',
 '?            ^                     ---- ^\n',
 '+   4. Complicated is better than complex.\n',
 '?           ++++ ^                      ^\n',
 '+   5. Flat is better than nested.\n']

В виде одной многострочной строки это выглядит следующим образом:

>>> import sys
>>> sys.stdout.writelines(result)
    1. Beautiful is better than ugly.
-   2. Explicit is better than implicit.
-   3. Simple is better than complex.
+   3.   Simple is better than complex.
?     ++
-   4. Complex is better than complicated.
?            ^                     ---- ^
+   4. Complicated is better than complex.
?           ++++ ^                      ^
+   5. Flat is better than nested.

Интерфейс командной строки для difflib

В этом примере показано, как использовать difflib для создания утилиты, подобной diff. Она также содержится в исходном коде Python как Tools/scripts/diff.py.

Вернуться на верх