difflib
— Помощники для вычисления дельт¶
Исходный код: Lib/difflib.py.
Этот модуль предоставляет классы и функции для сравнения последовательностей. Он может быть использован, например, для сравнения файлов и может выдавать информацию о различиях в файлах в различных форматах, включая HTML, контекстные и унифицированные диффы. Для сравнения каталогов и файлов см. также модуль filecmp
.
-
class
difflib.
SequenceMatcher
Это гибкий класс для сравнения пар последовательностей любого типа, при условии, что элементы последовательности hashable. Основной алгоритм предшествует алгоритму, опубликованному в конце 1980-х годов Рэтклиффом и Обершелпом под гиперболическим названием «гештальт-сопоставление шаблонов». Идея заключается в том, чтобы найти самую длинную непрерывную совпадающую подпоследовательность, которая не содержит «мусорных» элементов; эти «мусорные» элементы - те, которые неинтересны в каком-то смысле, например, пустые строки или пробелы. (Работа с мусором является расширением алгоритма Рэтклиффа и Обершелпа). Затем та же идея рекурсивно применяется к частям последовательностей слева и справа от совпадающей подпоследовательности. Это не дает минимальных последовательностей редактирования, но имеет тенденцию давать совпадения, которые «выглядят правильно» для людей.
Тайминг: Основной алгоритм Ratcliff-Obershelp имеет кубическое время в худшем случае и квадратичное время в ожидаемом случае.
SequenceMatcher
имеет квадратичное время в худшем случае и имеет поведение в ожидаемом случае, зависящее сложным образом от количества общих элементов последовательностей; в лучшем случае время линейно.Автоматическая эвристика хлама:
SequenceMatcher
поддерживает эвристику, которая автоматически рассматривает определенные элементы последовательности как хлам. Эвристика подсчитывает, сколько раз каждый отдельный элемент появляется в последовательности. Если дубликаты элемента (после первого) составляют более 1% от всей последовательности, а длина последовательности не менее 200 элементов, этот элемент помечается как «популярный» и рассматривается как хлам для целей подбора последовательности. Эту эвристику можно отключить, установив аргументautojunk
вFalse
при созданииSequenceMatcher
.Добавлено в версии 3.2: Параметр autojunk.
-
class
difflib.
Differ
¶ Это класс для сравнения последовательностей строк текста и получения человекочитаемых различий или дельт. Differ использует
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 - необязательные ключевые аргументы для указания строк заголовков колонок из/в файл (по умолчанию обе строки пустые).
context и numlines являются необязательными ключевыми аргументами. Установите context в
True
, если необходимо показать контекстные различия, иначе по умолчанию используетсяFalse
для показа полных файлов. Значение numlines по умолчанию равно5
. Когда context имеет значениеTrue
, numlines управляет количеством контекстных строк, которые окружают выделенные различия. Когда context имеет значениеFalse
numlines управляет количеством строк, которые показываются перед выделением различий при использовании гиперссылок «next» (при установке нуля гиперссылки «next» будут помещать следующее выделение различий в верхней части браузера без какого-либо ведущего контекста).Примечание
fromdesc и todesc интерпретируются как HTML без символов и должны быть правильно экранированы при получении входных данных из ненадежных источников.
Изменено в версии 3.5: Добавлен аргумент charset только для ключевого слова. Кодировка 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 (списки строк); верните дельту (generator, порождающую строки дельты) в формате context diff.
Контекстные диффы - это компактный способ показать только те строки, которые изменились, плюс несколько строк контекста. Изменения показываются в стиле «до/после». Количество контекстных строк задается параметром n, который по умолчанию равен трем.
По умолчанию управляющие строки diff (те, что имеют
***
или---
) создаются с пропущенной новой строкой. Это полезно для того, чтобы входы, созданные с помощьюio.IOBase.readlines()
, приводили к диффам, пригодным для использования сio.IOBase.writelines()
, поскольку и входы, и выходы имеют косую новую строку.Для входных данных, не содержащих концевых новых строк, установите аргумент lineterm в
""
, чтобы вывод был равномерно свободен от новых строк.Формат контекстных различий обычно содержит заголовок для имен файлов и времени модификации. Любой или все они могут быть указаны с помощью строк 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(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)¶ Возвращает список лучших «достаточно хороших» совпадений. слово - это последовательность, для которой нужны близкие совпадения (обычно строка), а возможности - это список последовательностей, с которыми можно сопоставить слово (обычно список строк).
Необязательный аргумент n (по умолчанию
3
) - это максимальное количество близких совпадений, которое нужно вернуть; n должно быть больше, чем0
.Необязательный аргумент cutoff (по умолчанию
0.6
) представляет собой плавающее число в диапазоне [0, 1]. Возможности, которые по крайней мере не похожи на слово, игнорируются.Лучшие (не более 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
-стиля (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)¶ Возвращает одну из двух последовательностей, породивших дельту.
Учитывая последовательность, созданную
Differ.compare()
или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 (списки строк); верните дельту (generator, порождающую строки дельты) в унифицированном формате diff.
Унифицированные диффы - это компактный способ показать только те строки, которые изменились, плюс несколько строк контекста. Изменения отображаются в стиле inline (вместо отдельных блоков before/after). Количество контекстных строк задается параметром n, который по умолчанию равен трем.
По умолчанию управляющие строки диффа (те, что имеют
---
,+++
или@@
) создаются с концевой новой строкой. Это полезно для того, чтобы входы, созданные с помощьюio.IOBase.readlines()
, приводили к диффам, пригодным для использования сio.IOBase.writelines()
, поскольку и входы, и выходы имеют косую новую строку.Для входных данных, не содержащих концевых новых строк, установите аргумент lineterm в
""
, чтобы вывод был равномерно свободен от новых строк.Формат контекстных различий обычно содержит заголовок для имен файлов и времени модификации. Любой или все они могут быть указаны с помощью строк 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 (списки байтовых объектов) с помощью dfunc; выдайте последовательность дельта-строк (также байтов) в формате, возвращаемом dfunc. dfunc должна быть вызываемой командой, обычно либо
unified_diff()
, либоcontext_diff()
.Позволяет сравнивать данные с неизвестной или непоследовательной кодировкой. Все входные данные, кроме n, должны быть байтовыми объектами, а не 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: Параметр autojunk.
Объекты SequenceMatcher получают три атрибута данных: bjunk - множество элементов b, для которых isjunk равно
True
; bpopular - множество элементов без мусора, которые эвристика считает популярными (если она не отключена); b2j - диктант, отображающий оставшиеся элементы 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()
и real_quick_ratio()
всегда как минимум больше, чем ratio()
:
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.quick_ratio()
0.75
>>> s.real_quick_ratio()
1.0
Примеры SequenceMatcher¶
Этот пример сравнивает две строки, считая пробелы «мусором»:
>>> 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
-генерируемые дельты не претендуют на то, чтобы быть минимальными диффами. Напротив, минимальные диффы часто противоречат интуиции, потому что они синхронизируются везде, где это возможно, иногда случайные совпадения на 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)
Далее мы создаем объект Differ:
>>> 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
.
#!/usr/bin/env python3
""" Command line interface to difflib.py providing diffs in four formats:
* ndiff: lists every line and highlights interline changes.
* context: highlights clusters of changes in a before/after format.
* unified: highlights clusters of changes in an inline format.
* html: generates side by side comparison with change highlights.
"""
import sys, os, difflib, argparse
from datetime import datetime, timezone
def file_mtime(path):
t = datetime.fromtimestamp(os.stat(path).st_mtime,
timezone.utc)
return t.astimezone().isoformat()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-c', action='store_true', default=False,
help='Produce a context format diff (default)')
parser.add_argument('-u', action='store_true', default=False,
help='Produce a unified format diff')
parser.add_argument('-m', action='store_true', default=False,
help='Produce HTML side by side diff '
'(can use -c and -l in conjunction)')
parser.add_argument('-n', action='store_true', default=False,
help='Produce a ndiff format diff')
parser.add_argument('-l', '--lines', type=int, default=3,
help='Set number of context lines (default 3)')
parser.add_argument('fromfile')
parser.add_argument('tofile')
options = parser.parse_args()
n = options.lines
fromfile = options.fromfile
tofile = options.tofile
fromdate = file_mtime(fromfile)
todate = file_mtime(tofile)
with open(fromfile) as ff:
fromlines = ff.readlines()
with open(tofile) as tf:
tolines = tf.readlines()
if options.u:
diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)
elif options.n:
diff = difflib.ndiff(fromlines, tolines)
elif options.m:
diff = difflib.HtmlDiff().make_file(fromlines,tolines,fromfile,tofile,context=options.c,numlines=n)
else:
diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)
sys.stdout.writelines(diff)
if __name__ == '__main__':
main()