Прежде чем приступать к программированию, необходимо выяснить, что значит сравнить два дерева каталогов. Если оба дерева имеют одинаковую структуру ветвей и глубину, проблема сводится к сравнению соответствующих файлов в каждом дереве. Однако в общем случае деревья могут иметь произвольную различную форму, глубину и так далее.
В более общем случае каталог в одном дереве может содержать больше или меньше элементов, чем соответствующий каталог в другом дереве. Если различие обусловлено наличием других файлов, это означает отсутствие соответствующих файлов для сравнения в другом каталоге. Если различие обусловлено наличием других каталогов, это означает отсутствие соответствующей ветви, в которую нужно войти. На самом деле единственный способ выявить файлы и каталоги, которые есть в одном дереве, но отсутствуют в другом, заключается в том, чтобы выявить различия в каталогах каждого уровня.
Иными словами, алгоритм сравнения деревьев должен также попутно выполнять сравнение каталогов. Начнем с реализации сравнения имен файлов для одного каталога, представленной в примере 6.11, так как это вложенная и более простая операция.
Пример 6.11. PP4E\System\Filetools\dirdiff.py
############################################################################## Порядок использования: python dirdiff.py dir1-path dir2-path
Сравнивает два каталога, пытаясь отыскать файлы, присутствующие в одном и отсутствующие в другом.
Эта версия использует функцию os.listdir и выполняет поиск различий между двумя списками. Обратите внимание, что сценарий проверяет только имена файлов, но не их содержимое, — версию, которая сравнивает результаты вызова методов ,read(),sbi найдете в сценарии diffall.py.
##############################################################################
import os, sys
def reportdiffs(unique1, unique2, dir1, dir2):
Генерирует отчет о различиях для одного каталога: часть вывода функции comparedirs
if not (unique1 or unique2):
print(‘Directory lists are identical’)
else:
if unique1:
print(‘Files unique to’, dir1)
for file in unique1:
print(‘,,,’, file)
if unique2:
print(‘Files unique to’, dir2)
for file in unique2: print(‘,,,’, file)
def difference(seq1, seq2):
Возвращает элементы, присутствующие только в seq1;
Операция set(seq1) — set(seq2) даст аналогичный результат, но множества являются неупорядоченными коллекциями, поэтому порядок следования элементов в каталоге будет утерян
return [item for item in seq1 if item not in seq2]
def comparedirs(dir1, dir2, files1=None, files2=None):
Сравнивает содержимое каталогов, но не сравнивает содержимое файлов; функции listdir может потребоваться передавать аргумент типа bytes, если могут встречаться имена файлов, недекодируемые на других платформах
print(‘Comparing’, dir1, ‘to’, dir2)
files1 = os.listdir(dir1) if files1 is None else files1
files2 = os.listdir(dir2) if files2 is None else files2
unique1 = difference(files1, files2)
unique2 = difference(files2, files1) reportdiffs(unique1, unique2, dir1, dir2) return not (unique1 or unique2) # true, если нет различий
def getargs():
“Аргументы при работе в режиме командной строки” try:
dir1, dir2 = sys.argv[1:] # 2 аргумента командной строки
except:
print(‘Usage: dirdiff.py dir1 dir2’)
sys.exit(1)
else:
return (dir1, dir2)
if __name__ == ‘__main__’: dir1, dir2 = getargs() comparedirs(dir1, dir2)
Получив списки имен для каждого каталога, этот сценарий просто выбирает уникальные имена в первом каталоге, уникальные имена во втором каталоге и сообщает о найденных уникальных именах как о расхождениях (то есть о файлах, имеющихся в одном каталоге, но отсутствующих в другом). Функция comparedirs возвращает значение True, если расхождения не были обнаружены, что полезно для обнаружения различий при вызове из других программ.
C:\…\PP4E\System\Filetools> dirdiff.py C:\temp\PP3E\Examples copytemp Comparing C:\temp\PP3E\Examples to copytemp Directory lists are identical
C:\…\PP4E\System\Filetools> dirdiff.py C:\temp\PP3E\Examples\PP3E\System ..
Comparing C:\temp\PP3E\Examples\PP3E\System to .. Files unique to C:\temp\PP3E\Examples\PP3E\System … App … Exits … Media … moreplus.py
Files unique to ..
… more.pyc … spam.txt … Tester … __init__.pyc
В основе сценария лежит функция difference: она реализует простую операцию сравнения списков. Применительно к каталогам, уникальные элементы представляют различия между деревьями, а общие элементы представляют имена файлов или подкаталогов, которые заслуживают дальнейшего сравнения или обхода. В Python 2.4 и более поздних версиях можно было бы использовать встроенные объекты типа set, если порядок следования имен в результатах не имеет значения — множества не являются последовательностями, поэтому они не сохраняют оригинальный порядок следования элементов в списках, полученных с помощью функции os.listdir. По этой причине (и чтобы не вынуждать пользователей модернизировать сценарий) вместо множеств мы будем использовать функцию, опирающуюся на использование выражения- генератора.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011