#python #diff #difflib
#python #разница #difflib
Вопрос:
Я просмотрел некоторые ответы на похожие вопросы здесь, но, думаю, я все еще чего-то не понимаю в том, как difflib.ndiff()
работает?
Я смотрю на ndiff
, в частности, потому, что документация подразумевает, что по умолчанию diff будет игнорировать изменения пробелов.
Вот простая программа, в которой я ожидаю, что строки в Differ (т. Е. возвращаемое значение из difflib.ndiff()
) будут пустыми:
import difflib
# a simple set of lines
A_LINES = [
'Line 1',
'Line 2',
]
# should be same as A_LINES if whitespace is ignored
B_LINES = [
' Line 1',
' Line 2',
]
def test_2(a, b):
# differ = difflib.ndiff(a, b)
differ = difflib.ndiff(a, b, charjunk=difflib.IS_CHARACTER_JUNK)
for line in differ:
print(line)
def main(a_fn, b_fn):
test_2(A_LINES, B_LINES)
if __name__ == '__main__':
main()
difflib.IS_CHARACTER_JUNK()
кажется, это просто предикат, который возвращает True
на ' '
и 't'
, False
иначе. Вызываете ли вы ndiff()
явным вызовом IS_CHARACTER_JUNK
или принимаете значение по умолчанию и не упоминаете charjunk
аргумент, я получаю тот же результат:
- Line 1
Line 1
?
- Line 2
Line 2
?
Это не тот результат, который я ожидал бы для diff, который игнорирует пробелы.
Это кажется мне очень неожиданным, учитывая документацию для ndiff (см.:https://docs.python.org/3/library/difflib.html). Документация отключена, или странная, или неправильная, или я просто чего-то не понимаю?
Как бы я вызвал ndiff()
такой, чтобы в генераторе ‘difference’ не было строк для этого примера?
Любая помощь, позволяющая лучше понять, как это сделать "ignore whitespace"-type diffs
, очень ценится.
Комментарии:
1. концепция «мусора» в difflib плохо документирована (как и его алгоритм в целом), но «мусор» на самом деле не игнорируется.
2. Я не думаю, что у difflib есть какие-либо функции для игнорирования различий.
3. В чем разница между использованием ndiff, принимающим функцию по умолчанию, которая обрабатывает пробел и табуляцию как ненужные, и явным предоставлением ей функции, которая безоговорочно возвращала бы False, если различия не игнорируются? Я не понимаю, что значит рассматривать определенный символ как «мусор», если такие различия все еще не игнорируются?
4. @user2357112 поддерживает ndiff-файлы ndiff и
SequenceMatcher
object для поддержки нежелательной строки и символов. но это просто не работает. Похоже на ошибку: IS_CHARACTER_JUNK (поведение по умолчанию) не отфильтровывает то, что он сообщает, что делает (пробел и табуляция). Вопрос все еще остается в силе (python 3.6, не проверял, было ли это исправлено в более новых версиях)
Ответ №1:
Кажется, что IS_CHARACTER_JUNK
функция фильтра вызывается, но не оказывает никакого влияния на фильтрацию нежелательных символов. Для меня это похоже на ошибку. Python 3.6 difflib по-прежнему ведет себя так же.
На данный момент я могу предложить приемлемый обходной путь: удалите конечные и начальные пробелы из строк и замените все повторяющиеся пробелы одним пробелом. Это обеспечивает, по крайней мере, эксплуатируемый вывод (удаление всех пробелов было бы непрактичным).
import difflib
import re
lines1 = ["foo bar ","cat ","nope"]
lines2 = ["foo bar ","hello","cat "]
def prefilter(line):
return re.sub("s "," ",line.strip())
for d in difflib.ndiff([prefilter(x) for x in lines1],[prefilter(x) for x in lines2]):
print(d)
результат (только добавленные / удаленные строки отображаются как изменения, строки с добавленными / удаленными пробелами — нет)
foo bar
hello
cat
- nope
Ответ №2:
Из просмотра исходного кода наhttps://github.com/python/cpython/blob/3.10/Lib/difflib.py , я понимаю, что используются как linejunk, так и charjunk, но таким образом, что мусор символов не имеет никакого эффекта. Это кажется скорее недостатком в логике, чем ошибкой как таковой.
Краткий ответ: нет, невозможно вызвать ndiff() таким образом, чтобы в генераторе различий в вашем примере не было строк.
Способ, которым это работает, заключается в следующем:
ndiff() делегирует Difference().compare() и передает как linkjunk, так и charjunk в Difference, который их сохраняет.
Differ.compare сначала определяет различия с помощью SequenceMatcher, передавая только функцию linejunk.
Когда он печатает различия, он использует _fancy_replace для различий в одной строке, используя ‘^’. Это использует SequenceMatcher (снова), но на этот раз передает функцию charjunk.
Однако для полных различий в строке он просто печатает ‘ ‘ или ‘-‘, никогда больше не вызывая SequenceMatcher.
Итак, для полных различий строк charjunk никогда не используется.
Если _fancy_replace действительно игнорирует пробелы при печати различий в одной строке, мы никогда не узнаем, потому что, когда есть разница в пробелах, первый проход с SequenceMatcher сгенерирует различия в полной строке, и _fancy_replace никогда не вызывается.
Вкратце: первый вызов SequenceMatcher исключает использование charjunk при вызове SequenceMatcher во второй раз, потому что он сгенерирует разницу в строке (с и -) и без вычурной разницы.
Нет, в документации об этом ничего не ясно.
Я надеюсь, что это улучшает ваше понимание.
Я не нашел другого способа сделать то, что вы просили, используя difflib, кроме перезаписи больших его частей.