# `difflib` — Helpers for computing deltas

Вихідний код: Lib/difflib.py

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

class `difflib.``SequenceMatcher`

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

  Час: основний алгоритм Реткліффа-Обершелпа – це кубічний час у найгіршому випадку та квадратичний час у очікуваному випадку. `SequenceMatcher` — це квадратичний час для найгіршого випадку та має очікувану поведінку, складно залежну від кількості спільних елементів послідовностей; найкращий час – лінійний.

  Автоматична евристика сміття: `SequenceMatcher` підтримує евристику, яка автоматично розглядає певні елементи послідовності як сміття. Евристика підраховує, скільки разів кожен окремий елемент з’являється в послідовності. Якщо дублікати елемента (після першого) становлять більше 1% послідовності, а послідовність складається щонайменше з 200 елементів, цей елемент позначається як «популярний» і розглядається як небажаний для цілей відповідності послідовності. Цю евристику можна вимкнути, встановивши для аргументу `autojunk` значення `False` під час створення `SequenceMatcher`.

  Нове в версії 3.2: The autojunk parameter.

class `difflib.``Differ`

  Це клас для порівняння послідовностей рядків тексту та створення зрозумілих людині відмінностей або дельт. Differ використовує `SequenceMatcher` як для порівняння послідовностей рядків, так і для порівняння послідовностей символів у подібних (майже відповідних) рядках.

  Кожен рядок дельти `Differ` починається двобуквеним кодом:

  Код | Значення
  --- | ---
  `'- ''` | рядок, унікальний для послідовності 1
  `'+ '` | рядок, унікальний для послідовності 2
  `' '` | лінія, спільна для обох послідовностей
  `'? ''` | рядок відсутній у жодній послідовності введення

  Lines beginning with „`?`“ attempt to guide the eye to intraline differences, and were not present in either input sequence. These lines can be confusing if the sequences contain tab characters.

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

    Примітка

    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` is a command-line front-end to this class and contains a good example of its use.

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

  Порівняти a і b (списки рядків); повертає дельту (generator, що генерує дельта-рядки) у форматі контекстної різниці.

  Контекстні відмінності — це компактний спосіб показати лише рядки, які змінилися, плюс кілька рядків контексту. Зміни відображаються у стилі до/після. Кількість рядків контексту встановлюється 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(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` is a command-line front-end to this function.

      >>> 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, що генерує дельта-лінії) в уніфікованому форматі різниці.

  Уніфіковані відмінності — це компактний спосіб показати лише рядки, які змінилися, плюс кілька рядків контексту. Зміни відображаються у вбудованому стилі (замість окремих блоків до/після). Кількість рядків контексту встановлюється 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 (списки об’єктів 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` для ігнорованих рядків. Рядок рядок ігнорується, якщо рядок порожній або містить один `'#'`, інакше він не ігнорується. Використовується за замовчуванням для параметра linejunk у `ndiff()` у старих версіях.

`difflib.``IS_CHARACTER_JUNK`(ch)

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

Дивись також

Pattern Matching: The Gestalt Approach

  Discussion of a similar algorithm by John W. Ratcliff and D. E. Metzener. This was published in Dr. Dobb’s Journal in July, 1988.

## Об’єкти SequenceMatcher

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

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

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

      lambda x: x in " \t"

  якщо ви порівнюєте рядки як послідовності символів і не хочете синхронізувати пробіли чи жорсткі табуляції.

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

  Необов’язковий аргумент autojunk можна використовувати для вимкнення автоматичної евристики сміття.

  Нове в версії 3.2: The autojunk parameter.

  Об’єкти 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` and `blo <= j <= j+k <= bhi`. For all `(i', j', k')` meeting those conditions, the additional conditions `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.

    Значення тегу є рядками з такими значеннями:

    Значення | Значення
    --- | ---
    `'замінити'` | `a[i1:i2]` слід замінити на `b[j1:j2]`.
    `'видалити'` | `a[i1:i2]` слід видалити. Зверніть увагу, що в цьому випадку `j1 == j2`.
    `'вставити'` | `b[j1:j2]` слід вставити в `a[i1:i1]`. Зверніть увагу, що в цьому випадку `i1 == i2`.
    `'рівний''` | `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()`.

The three methods that return the ratio of matching to total characters can give different results due to differing levels of approximation, although `quick_ratio()` and `real_quick_ratio()` are always at least as large as `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()` returns a float in [0, 1], measuring the similarity of the sequences. As a rule of thumb, a `ratio()` value over 0.6 means the sequences are close matches:

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

If you’re only interested in where the sequences match, `get_matching_blocks()` is handy:

    >>> 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

Note that the last tuple returned by `get_matching_blocks()` is always a dummy, `(len(a), len(b), 0)`, and this is the only case in which the last tuple element (number of elements matched) is `0`.

If you want to know how to change the first sequence into the second, use `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 for a small application built with `SequenceMatcher`.

## Різні об’єкти

Зауважте, що `Differ`-генеровані дельти не претендують на мінімальні відмінності. Навпаки, мінімальні відмінності часто суперечать інтуїції, оскільки вони синхронізуються будь-де, де можливо, іноді випадково збігаються на 100 сторінках одна від одної. Обмеження точок синхронізації суміжними збігами зберігає деяке уявлення про локальність за рахунок випадкових витрат на створення довшої різниці.

Клас `Differ` має такий конструктор:

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

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

  linejunk: функція, яка приймає один рядковий аргумент і повертає істину, якщо рядок є небажаним. Типовим значенням є `None`, що означає, що жоден рядок не вважається небажаним.

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

  Ці функції фільтрації сміття пришвидшують зіставлення для пошуку відмінностей і не призводять до ігнорування будь-яких відмінних рядків чи символів. Щоб отримати пояснення, прочитайте опис параметра isjunk методу `find_longest_match()`.

  Об’єкти `Differ` використовуються (генеруються дельти) за допомогою одного методу:

  `compare`(a, b)

    Порівняйте дві послідовності рядків і згенеруйте дельту (послідовність рядків).

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

## Різний приклад

This example compares two texts. First we set up the texts, sequences of individual single-line strings ending with newlines (such sequences can also be obtained from the `readlines()` method of file-like objects):

    >>> 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))

`результат` - це список рядків, тож давайте красиво надрукуємо його:

    >>> 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

This example shows how to use difflib to create a `diff`-like utility. It is also contained in the Python source distribution, as `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()