Какой самый эффективный способ проверить наличие дублирующих данных между несколькими файлами? - PullRequest
0 голосов
/ 24 октября 2018

Допустим, у вас есть папка с сотнями или тысячами .csv или .txt файлов, которые предположительно содержат различную информацию, но вы хотите убедиться, что joe041.txt на самом деле не содержит те же данные, что и joe526.txt.авария.

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

Есть ли более эффективный способ сделать это?

Даже использование filecmp для этого кажется менее эффективным, поскольку модуль имеет только файл против файла и dir против dir сравнений, но нет команд file vs dir - это означает, что для его использования вам нужно будет выполнить итерацию по x ² раз (все файлы в dir против всех других файлов в dir).

import os
import hashlib

outputfile = []

for x in(os.listdir("D:/Testing/New folder")):
    with open("D:/Testing/New folder/%s" % x, "rb") as openfile:
        text=openfile.read()
        outputfile.append(x)
        outputfile.append(",")
        outputfile.append(hashlib.md5(text).hexdigest())
        outputfile.append("\n")

print(outputfile)

with open("D:/Testing/New folder/output.csv","w") as openfile:
    for x in outputfile:
        openfile.write(x)

1 Ответ

0 голосов
/ 24 октября 2018

Вдохновленный комментариями @ 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)
...