Spec-Zone.ru › Python 2.7

difflib — Помощники для вычисления разниц

Новое в версии 2.1.

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

class difflib.SequenceMatcher

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

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

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

Новое в версии 2.7.1: Параметр 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][, numlines])

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

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

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

make_table(fromlines, tolines [, fromdesc][, todesc][, context][, numlines])

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

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

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

Новое в версии 2.4.

difflib.context_diff(a, b[, fromfile][, tofile][, fromfiledate][, tofiledate][, n][, lineterm])

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

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

По умолчанию строки управления диффом (те, которые содержат *** или ---) создаются с завершающей новой строкой. Это полезно, чтобы входные данные, созданные из file.readlines(), привели к диффам, пригодным для использования с file.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']
>>> for line in context_diff(s1, s2, fromfile='before.py', tofile='after.py'):
...     sys.stdout.write(line)  # doctest: +NORMALIZE_WHITESPACE
*** before.py
--- after.py
***************
*** 1,4 ****
! bacon
! eggs
! ham
  guido
--- 1,4 ----
! python
! eggy
! hamster
  guido

См. Командный интерфейс difflib для более подробного примера.

Новое в версии 2.3.

difflib.get_close_matches(word, possibilities[, n][, cutoff])

Возвращает список наилучших «достаточно хороших» совпадений. 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('apple', keyword.kwlist)
[]
>>> get_close_matches('accept', keyword.kwlist)
['except']
END_OF_DOCUMENT_MARKER ```
difflib.ndiff(a, b[, linejunk][, charjunk])

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

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

linejunk: Функция, принимающая одну строку в качестве аргумента и возвращающая true, если строка — мусор, или false, если нет. По умолчанию — (None), начиная с Python 2.3. Раньше по умолчанию была функция модуля IS_LINE_JUNK(), которая фильтрует строки без видимых символов, кроме, самое большее, одного символа решётки ('#'). Начиная с Python 2.3, базовый класс SequenceMatcher выполняет динамический анализ, какие строки настолько часты, что представляют собой шум, и это обычно работает лучше, чем значение по умолчанию до версии 2.3.

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

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

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

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

Используя sequence, полученный из Differ.compare() или ndiff(), извлечь строки, происходящие из файла 1 или 2 (параметр which), удалив префиксы строк.

Пример:

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

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

Unified diff — компактный способ показать только строки, которые изменились, плюс несколько строк контекста. Изменения показаны в инлайновом стиле (вместо отдельных блоков до/после). Количество строк контекста задаётся параметром n, который по умолчанию равен трём.

По умолчанию, строки управления разностью (те, которые содержат ---, +++, или @@), создаются с заключительным символом перевода строки. Это полезно, чтобы входные данные, созданные из file.readlines(), давали разности, пригодные для использования с file.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']
>>> for line in unified_diff(s1, s2, fromfile='before.py', tofile='after.py'):
...     sys.stdout.write(line)   # doctest: +NORMALIZE_WHITESPACE
--- before.py
+++ after.py
@@ -1,4 +1,4 @@
-bacon
-eggs
-ham
+python
+eggy
+hamster
 guido

См. Командная строка интерфейса difflib для более подробного примера.

Введено в версии 2.3.

difflib.IS_LINE_JUNK(line)

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

difflib.IS_CHARACTER_JUNK(ch)

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

См. также

Pattern Matching: The Gestalt Approach

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

1. Объекты SequenceMatcher

Класс SequenceMatcher имеет такой конструктор:

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

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

lambda x: x in " \t"

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

Дополнительные аргументы a и b — это последовательности, которые нужно сравнить; оба по умолчанию равны пустым строкам. Элементы обеих последовательностей должны быть хешируемыми.

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

Новое в версии 2.7.1: Параметр autojunk.

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

set_seqs(a, b)

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

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

set_seq1(a)

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

set_seq2(b)

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

find_longest_match(alo, ahi, blo, bhi)

Найти самый длинный совпадающий блок в 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).

Изменено в версии 2.6: Этот метод возвращает именованную кортеж Match(a, b, size).

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'; другими словами, смежные тройки всегда описывают несмежные равные блоки.

Изменено в версии 2.5: Была реализована гарантия, что смежные тройки всегда описывают несмежные блоки.

>>> 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 ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
...           (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))
 delete a[0:1] (q) b[0:0] ()
  equal a[1:3] (ab) b[0:2] (ab)
replace a[3:4] (x) b[2:3] (y)
  equal a[4:6] (cd) b[3:5] (cd)
 insert a[6:6] () b[5:6] (f)
get_grouped_opcodes([n])

Возвращает генератор групп с до n строк контекста.

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

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

Новое в версии 2.3.

ratio()

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

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

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

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

2. Примеры 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, может быть использован для выполнения полезной работы.
  • Рецепт простой системы контроля версий для небольшого приложения, построенного с помощью SequenceMatcher.

3. Объекты Differ

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

Класс Differ имеет следующий конструктор:

class difflib.Differ([linejunk[, charjunk]])

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

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

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

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

compare(a, b)

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

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

4. Пример Differ

В этом примере сравниваются два текста. Сначала мы задаем тексты, последовательности отдельных строк с новой строкой в конце (такие последовательности также могут быть получены с помощью метода 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(1)
>>> 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(1)

Далее мы создаём объект 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.

5. Командная строка интерфейса difflib

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

""" 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, time, difflib, optparse

def main():
     # Configure the option parser
    usage = "usage: %prog [options] fromfile tofile"
    parser = optparse.OptionParser(usage)
    parser.add_option("-c", action="store_true", default=False,
                      help='Produce a context format diff (default)')
    parser.add_option("-u", action="store_true", default=False,
                      help='Produce a unified format diff')
    hlp = 'Produce HTML side by side diff (can use -c and -l in conjunction)'
    parser.add_option("-m", action="store_true", default=False, help=hlp)
    parser.add_option("-n", action="store_true", default=False,
                      help='Produce a ndiff format diff')
    parser.add_option("-l", "--lines", type="int", default=3,
                      help='Set number of context lines (default 3)')
    (options, args) = parser.parse_args()

    if len(args) == 0:
        parser.print_help()
        sys.exit(1)
    if len(args) != 2:
        parser.error("need to specify both a fromfile and tofile")

    n = options.lines
    fromfile, tofile = args # as specified in the usage string

    # we're passing these as arguments to the diff function
    fromdate = time.ctime(os.stat(fromfile).st_mtime)
    todate = time.ctime(os.stat(tofile).st_mtime)
    with open(fromfile, 'U') as f:
        fromlines = f.readlines()
    with open(tofile, 'U') as f:
        tolines = f.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)

    # we're using writelines because diff is a generator
    sys.stdout.writelines(diff)

if __name__ == '__main__':
    main()

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/2.7/library/difflib.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API