Python3: рекурсивное сравнение двух каталогов на основе содержимого файла - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть две директории, содержащие кучу файлов и подпапок.Я хотел бы проверить, являются ли содержимое файла одинаковыми в обоих каталогах (игнорируя имя файла).Структура подпапок тоже должна быть такой же.

Я посмотрел на filecmp.dircmp, но это не помогает, потому что не учитывает содержимое файла;нет опции shallow=False с filecmp.dircmp(), см. здесь .

Обходной путь в этом SO ответе также не работает, поскольку он учитывает имена файлов.

Какой лучший способ сделать мое сравнение?

1 Ответ

0 голосов
/ 11 февраля 2019

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

import filecmp
import os
from collections import defaultdict
from sys import argv

def compareDirs(d1,d2):
    files1 = defaultdict(set)
    files2 = defaultdict(set)
    subd1  = set()
    subd2  = set()
    for entry in os.scandir(d1):
        if entry.is_dir(): subd1.add(entry)
        else: files1[os.path.getsize(entry)].add(entry)
    #Collecting first to compare length since we are guessing no
    #match is more likely. Can compare files directly if this is
    # not true.
    for entry in os.scandir(d2):
        if entry.is_dir(): subd2.add(entry)
        else: files2[os.path.getsize(entry)].add(entry)

    #Structure not the same. Checking prior to content.
    if len(subd1) != len(subd2) or len(files1) != len(files2): return False

    for size in files2:
        for entry in files2[size]:
            for fname in files1[size]: #If size does not exist will go to else
                if filecmp.cmp(fname,entry,shallow=False): break
            else: return False
            files1[size].remove(fname)
            if not files1[size]: del files1[size]

    #Missed a file
    if files1: return False

    #This is enough since we checked lengths - if all sd2 are matched, sd1
    #will be accounted for.
    for sd1 in subd1:
        for sd2 in subd2:
            if compareDirs(sd1,sd2): break
        else: return False #Did not find a sub-directory
        subd2.remove(sd2)

    return True

print(compareDirs(argv[1],argv[2]))

Рекурсивно вводите оба каталога.Сравнить файлы на первом уровне - не удастся, если нет совпадений.Затем попробуйте сопоставить любой вложенный каталог в первом каталоге с любым вложенным каталогом в следующем, пока все не будут сопоставлены.

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

Конечно, в случаенесколько подкаталогов с точно такими же структурами и размерами, нам все равно нужно сравнить все возможности сопоставления.

...