Поиск дубликатов файлов через hashlib?

Я знаю, что этот вопрос задавали раньше, и я видел некоторые ответы, но этот вопрос касается моего кода и наилучшего способа решения этой задачи.

Я хочу сканировать каталог и посмотреть, есть ли в этом каталоге дубликаты (путем проверки хешей MD5). Ниже приведен мой код:

import sys import os import hashlib fileSliceLimitation = 5000000 #bytes # if the file is big, slice trick to avoid to load the whole file into RAM def getFileHashMD5(filename): retval = 0; filesize = os.path.getsize(filename) if filesize > fileSliceLimitation: with open(filename, 'rb') as fh: m = hashlib.md5() while True: data = fh.read(8192) if not data: break m.update(data) retval = m.hexdigest() else: retval = hashlib.md5(open(filename, 'rb').read()).hexdigest() return retval searchdirpath = raw_input("Type directory you wish to search: ") print "" print "" text_file = open('outPut.txt', 'w') for dirname, dirnames, filenames in os.walk(searchdirpath): # print path to all filenames. for filename in filenames: fullname = os.path.join(dirname, filename) h_md5 = getFileHashMD5 (fullname) print h_md5 + " " + fullname text_file.write("\n" + h_md5 + " " + fullname) # close txt file text_file.close() print "\n\n\nReading outPut:" text_file = open('outPut.txt', 'r') myListOfHashes = text_file.read() if h_md5 in myListOfHashes: print 'Match: ' + " " + fullname 

Это дает мне следующий результат:

 Please type in directory you wish to search using above syntax: /Users/bubble/Desktop/aF 033808bb457f622b05096c2f7699857v /Users/bubble/Desktop/aF/.DS_Store 409d8c1727960fddb7c8b915a76ebd35 /Users/bubble/Desktop/aF/script copy.py 409d8c1727960fddb7c8b915a76ebd25 /Users/bubble/Desktop/aF/script.py e9289295caefef66eaf3a4dffc4fe11c /Users/bubble/Desktop/aF/simpsons.mov Reading outPut: Match: /Users/bubble/Desktop/aF/simpsons.mov 

Моя идея:

1) Каталог сканирования 2) Запишите хеши MD5 + Имя файла в текстовый файл. 3) Откройте текстовый файл только для чтения. 4) Сканируйте каталог AGAIN и проверьте файл с текстом …

Я вижу, что это не очень хороший способ сделать это, и это не сработает. «Match» просто распечатывает последний обработанный файл.

Как я могу заставить этот скрипт найти дубликаты? Может ли кто-нибудь сказать мне лучший / более простой способ решения этой задачи.

Большое спасибо за любую помощь. Извините, это длинный пост.

  • Python - Лучшая библиотека графического интерфейса для работы?
  • Вход PyAudio переполнен
  • Как бы вы проанализировали отступы (стиль python)?
  • постоянно контролировать программу / процесс с помощью python
  • Обработка определенного типа исключения в python
  • Преобразование списка датестей в datetime очень медленно с помощью Python strptime
  • Python: не может назначать литерал
  • Как перемещать файлы между двумя ведрами Amazon S3 с помощью boto?
  • 3 Solutions collect form web for “Поиск дубликатов файлов через hashlib?”

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

     from collections import defaultdict file_dict = defaultdict(list) for filename in files: file_dict[get_file_hash(filename)].append(filename) 

    В конце этого процесса file_dict будет содержать список для каждого уникального хэша; когда два файла имеют одинаковый хеш, они оба появятся в списке для этого хеша. Затем отфильтруйте dict, ища списки значений длиннее 1, и сравните файлы, чтобы убедиться, что они одинаковые – что-то вроде этого:

     for duplicates in file_dict.values(): # file_dict.itervalues() in Python 2 if len(duplicates) > 1: # double-check reported duplicates and generate output 

    Или это:

     duplicates = [files for files in file_dict.values() if len(files) > 1] 

    get_file_hash может использовать MD5; или он может просто получить первый и последний байты файла, как предложил Рамчандра Апте в комментариях выше; или он может просто использовать размеры файлов, как предложил tdelaney в комментариях выше. Однако каждая из последних двух стратегий, скорее всего, создает ложные срабатывания. Вы можете комбинировать их, чтобы уменьшить ложную положительную скорость.

    Если вы работаете с очень большим количеством файлов, вы можете использовать более сложную структуру данных, такую ​​как Bloom Filter .

    @senderle имеет отличный ответ, но, поскольку он упомянул, что мое решение создаст ложные срабатывания, я подумал, что перчатка была заложена, и мне лучше показать какой-то код. Я уменьшил вашу функцию md5 (она всегда должна использовать случай «fileSliceLimitation» и должна быть менее скупой с его входным буфером), а затем предварительно фильтруется размером до выполнения md5.

     import sys import os import hashlib from collections import defaultdict searchdirpath = sys.argv[1] size_map = defaultdict(list) def getFileHashMD5(filename): m = hashlib.md5() with open(filename, 'rb', 1024*1024) as fh: while True: data = fh.read(1024*1024) if not data: break m.update(data) return m.hexdigest() # group files by size for dirname, dirnames, filenames in os.walk(searchdirpath): for filename in filenames: fullname = os.path.join(dirname, filename) size_map[os.stat(fullname).st_size].append(fullname) # scan files of same size for fullnames in size_map.itervalues(): if len(fullnames) > 0: hash_map = defaultdict(list) for fullname in fullnames: hash_map[getFileHashMD5(fullname)].append(fullname) for fullnames in hash_map.itervalues(): if len(fullnames) > 1: print "duplicates:" for fullname in fullnames: print " ", fullname 

    (РЕДАКТИРОВАТЬ)

    Было несколько вопросов об этой реализации, на которые я попытаюсь ответить здесь:

    1) почему (1024 * 1024) размер не «5000000»

    Ваш исходный код читается в 8192 (8 KiB) приращениях, что очень мало для современных систем. Вы, скорее всего, получите лучшую производительность, захватив больше сразу. 1024 * 1024 – это 1048576 (1 MiB) байт, и это было просто предположение относительно разумного числа. Что касается того, почему я написал это таким странным образом, 1000 (десятичный килобайт) любят люди, но 1024 (бинарный кибибит) любят компьютеры и файловые системы. У меня есть привычка писать some_number*1024 поэтому легко видеть, что я имею в виду 1 шаг KiB. 5000000 также является разумным числом, но вы должны рассмотреть 5 * 1024 * 1024 (это 5 MiB), чтобы вы получили то, что хорошо выровнено для файловой системы.

    2) что делает этот бит точно: size_map = defaultdict (list)

    Он создает «defaultdict», который добавляет функциональность в обычный объект dict. Обычный dict вызывает исключение KeyError, когда он индексируется несуществующим ключом. defaultdict создает значение по умолчанию и вместо этого добавляет эту пару ключ / значение в dict. В нашем случае size_map[some_size] говорит: «Дайте мне список файлов some_size и создайте новый пустой список, если у вас его нет».

    size_map[os.stat(fullname).st_size].append(fullname) . Это сводится к:

     stat = os.stat(fullname) size = stat.st_size filelist = size_map[size] # this is the same as: # if size not in size_map: # size_map[size] = list() # filelist = size_map[size] filelist.append(fullname) 

    3) sys.argv [1] Я предполагаю, что sys.argv [1] просто делает аргумент python py.py 'filepath' (где filepath – argv [1]?

    Да, когда вы вызываете скрипт python, sys.argv [0] – это имя скрипта, а sys.argv [1:] (arg 1 и следующий) – любые дополнительные аргументы, приведенные в командной строке. Я использовал sys.argv [1] как быстрый способ протестировать скрипт, когда я его написал, и вы должны изменить это, чтобы удовлетворить ваши потребности.

    Первое, что вам нужно сделать, – это сохранить h_md5 в списке при прохождении через ваши файлы. Что-то вроде:

     h_md5=[] 

    прежде чем вы будете проходить через ваш каталог. А также

     h_md5.append(getFileHashMD5(fullname)) 

    внутри вашей петли. Теперь у вас есть список хешей для сравнения с вашим выходным файлом, а не просто последний, который вы сделали в своем цикле.

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

    edit: ответ выше @senderle – гораздо лучший способ сделать это, если вы хотите изменить свой код.

    Python - лучший язык программирования в мире.