Вдохновленный комментариями @ sɐunıɔ ןɐ qɐp, вы можете попробовать итеративный подход, который сначала выполняет дешевые операции со всеми файлами (определите размер файла), а затем проводит более глубокое сравнение с теми файлами, которые имеют одинаковые размеры.
Этот код сравнивает сначала размер, затем первые строки файлов и, наконец, md5
хэш всего файла.Вы можете адаптировать его так, как считаете нужным.
Я использую длинные имена переменных, чтобы сделать их явными;не отвлекайся на это.
import os
import hashlib
def calc_md5(file_path):
hash_md5 = hashlib.md5()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b''):
hash_md5.update(chunk)
return hash_md5.hexdigest()
def get_duplicates_by_size(dir_path):
files_by_size = {}
for elem in os.listdir(dir_path):
file_path = os.path.join(dir_path, elem)
if os.path.isfile(file_path):
size = os.stat(file_path).st_size
if size not in files_by_size:
files_by_size[size] = []
files_by_size[size].append(file_path)
# keep only entries with more than one file;
# the others don't need to be kept in memory
return {
size: file_list
for size, file_list in files_by_size.items()
if len(file_list) > 1}
def get_duplicates_by_first_content(files_by_size, n_chars):
files_by_size_and_first_content = {}
for size, file_list in files_by_size.items():
d = {}
for file_path in file_list:
with open(file_path) as f:
first_content = f.read(n_chars)
if first_content not in d:
d[first_content] = []
d[first_content].append(file_path)
# keep only entries with more than one file;
# the others don't need to be kept in memory
d = {
(size, first_content): file_list_2
for first_content, file_list_2 in d.items()
if len(file_list_2) > 1}
files_by_size_and_first_content.update(d)
return files_by_size_and_first_content
def get_duplicates_by_hash(files_by_size_and_first_content):
files_by_size_and_first_content_and_hash = {}
for (size, first_content), file_list in files_by_size_and_first_content.items():
d = {}
for file_path in file_list:
file_hash = calc_md5(file_path)
if file_hash not in d:
d[file_hash] = []
d[file_hash].append(file_path)
# keep only entries with more than one file;
# the others don't need to be kept in memory
d = {
(size, first_content, file_hash): file_list_2
for file_hash, file_list_2 in d.items()
if len(file_list_2) > 1}
files_by_size_and_first_content_and_hash.update(d)
return files_by_size_and_first_content_and_hash
if __name__ == '__main__':
r = get_duplicates_by_size('D:/Testing/New folder')
r = get_duplicates_by_first_content(r, 20) # customize the number of chars to read
r = get_duplicates_by_hash(r)
for k, v in r.items():
print('Key:', k)
print(' Files:', v)