difflib
— Helpers for computing deltas¶
Вихідний код: Lib/difflib.py
Цей модуль містить класи та функції для порівняння послідовностей. Він може бути використаний, наприклад, для порівняння файлів і може видавати інформацію про відмінності файлів у різних форматах, включаючи HTML, контекст і уніфіковані відмінності. Для порівняння каталогів і файлів див. також модуль filecmp
.
- class difflib.SequenceMatcher
Це гнучкий клас для порівняння пар послідовностей будь-якого типу, за умови, що елементи послідовності hashable. Базовий алгоритм передує алгоритму, опублікованому наприкінці 1980-х років Реткліфом і Обершелпом під гіперболічною назвою «відповідність гештальт-шаблону», і є трохи моднішим за нього. Ідея полягає в тому, щоб знайти найдовшу безперервну відповідну підпослідовність, яка не містить «сміттєвих» елементів; ці «сміттєві» елементи є такими, що в певному сенсі нецікаві, наприклад порожні рядки чи пробіли. (Обробка сміття є розширенням алгоритму Раткліффа та Обершелпа.) Ця сама ідея потім рекурсивно застосовується до фрагментів послідовностей ліворуч і праворуч від відповідної підпослідовності. Це не дає мінімальних послідовностей редагування, але, як правило, дає збіги, які «виглядають правильно» для людей.
Час: основний алгоритм Реткліффа-Обершелпа – це кубічний час у найгіршому випадку та квадратичний час у очікуваному випадку.
SequenceMatcher
— це квадратичний час для найгіршого випадку та має очікувану поведінку, складно залежну від кількості спільних елементів послідовностей; найкращий час – лінійний.Автоматична евристика сміття:
SequenceMatcher
підтримує евристику, яка автоматично розглядає певні елементи послідовності як сміття. Евристика підраховує, скільки разів кожен окремий елемент з’являється в послідовності. Якщо дублікати елемента (після першого) становлять більше 1% послідовності, а послідовність складається щонайменше з 200 елементів, цей елемент позначається як «популярний» і розглядається як небажаний для цілей відповідності послідовності. Цю евристику можна вимкнути, встановивши для аргументуautojunk
значенняFalse
під час створенняSequenceMatcher
.Змінено в версії 3.2: Added 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 whitespace characters, such as spaces, tabs or line breaks.
- 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()
.
- 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. Якщо не вказано, рядки за умовчанням пусті.
>>> 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)¶
Поверніть список найкращих «досить хороших» збігів. слово — це послідовність, для якої потрібні близькі збіги (зазвичай це рядок), а можливості — це список послідовностей, з якими потрібно зіставити слово (зазвичай список рядків).
Необов’язковий аргумент 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()
, яка відфільтровує пробіли (пробіл або символ табуляції; це погана ідея включати новий рядок у це!).>>> 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 значення
""
, щоб вихідні дані не містили символів нового рядка.The unified diff format normally has a header for filenames and modification times. Any or all of these may be specified using strings for fromfile, tofile, fromfiledate, and tofiledate. The modification times are normally expressed in the ISO 8601 format. If not specified, the strings default to blanks.
>>> 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.Added in version 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: Added the autojunk parameter.
Об’єкти SequenceMatcher отримують три атрибути даних: bjunk — це набір елементів b, для яких isjunk має значення
True
; bpopular — це набір непотрібних елементів, які вважаються популярними за евристикою (якщо її не вимкнено); b2j — це dict, що відображає решту елементів b на список позицій, де вони зустрічаються. Усі три скидаються щоразу, коли b скидається за допомогоюset_seqs()
абоset_seq2()
.Added in version 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
andblo <= j <= j+k <= bhi
. For all(i', j', k')
meeting those conditions, the additional conditionsk > = 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
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.
""" 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()
ndiff example¶
This example shows how to use difflib.ndiff()
.
"""ndiff [-q] file1 file2
or
ndiff (-r1 | -r2) < ndiff_output > file1_or_file2
Print a human-friendly file difference report to stdout. Both inter-
and intra-line differences are noted. In the second form, recreate file1
(-r1) or file2 (-r2) on stdout, from an ndiff report on stdin.
In the first form, if -q ("quiet") is not specified, the first two lines
of output are
-: file1
+: file2
Each remaining line begins with a two-letter code:
"- " line unique to file1
"+ " line unique to file2
" " line common to both files
"? " line not present in either input file
Lines beginning with "? " attempt to guide the eye to intraline
differences, and were not present in either input file. These lines can be
confusing if the source files contain tab characters.
The first file can be recovered by retaining only lines that begin with
" " or "- ", and deleting those 2-character prefixes; use ndiff with -r1.
The second file can be recovered similarly, but by retaining only " " and
"+ " lines; use ndiff with -r2; or, on Unix, the second file can be
recovered by piping the output through
sed -n '/^[+ ] /s/^..//p'
"""
__version__ = 1, 7, 0
import difflib, sys
def fail(msg):
out = sys.stderr.write
out(msg + "\n\n")
out(__doc__)
return 0
# open a file & return the file object; gripe and return 0 if it
# couldn't be opened
def fopen(fname):
try:
return open(fname)
except IOError as detail:
return fail("couldn't open " + fname + ": " + str(detail))
# open two files & spray the diff to stdout; return false iff a problem
def fcompare(f1name, f2name):
f1 = fopen(f1name)
f2 = fopen(f2name)
if not f1 or not f2:
return 0
a = f1.readlines(); f1.close()
b = f2.readlines(); f2.close()
for line in difflib.ndiff(a, b):
print(line, end=' ')
return 1
# crack args (sys.argv[1:] is normal) & compare;
# return false iff a problem
def main(args):
import getopt
try:
opts, args = getopt.getopt(args, "qr:")
except getopt.error as detail:
return fail(str(detail))
noisy = 1
qseen = rseen = 0
for opt, val in opts:
if opt == "-q":
qseen = 1
noisy = 0
elif opt == "-r":
rseen = 1
whichfile = val
if qseen and rseen:
return fail("can't specify both -q and -r")
if rseen:
if args:
return fail("no args allowed with -r option")
if whichfile in ("1", "2"):
restore(whichfile)
return 1
return fail("-r value must be 1 or 2")
if len(args) != 2:
return fail("need 2 filename args")
f1name, f2name = args
if noisy:
print('-:', f1name)
print('+:', f2name)
return fcompare(f1name, f2name)
# read ndiff output from stdin, and print file1 (which=='1') or
# file2 (which=='2') to stdout
def restore(which):
restored = difflib.restore(sys.stdin.readlines(), which)
sys.stdout.writelines(restored)
if __name__ == '__main__':
main(sys.argv[1:])