2

この質問は以前に尋ねられたことを知っており、いくつかの回答を見てきましたが、この質問は私のコードと、このタスクを達成するための最良の方法に関するものです。

ディレクトリをスキャンし、(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) ディレクトリを再度スキャンし、テキスト ファイルと照合する...

これは良い方法ではなく、機能しないことがわかります。「一致」は、処理された最後のファイルを出力するだけです。

このスクリプトで実際に重複を見つけるにはどうすればよいですか? 誰かがこのタスクを達成するためのより良い/より簡単な方法を教えてもらえますか.

助けてくれてありがとう。申し訳ありませんが、これは長い投稿です。

4

3 に答える 3

5

重複を識別する明白なツールは、ハッシュ テーブルです。非常に多数のファイルを扱っていない限り、次のようなことができます。

from collections import defaultdict

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

このプロセスの最後に、file_dictすべての一意のハッシュのリストが含まれます。2 つのファイルが同じハッシュを持つ場合、それらは両方ともそのハッシュのリストに表示されます。次に、1 より長い値リストを探して dict をフィルター処理し、ファイルを比較して、それらが同じであることを確認します。次のようになります。

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_hashMD5 を使用できます。または、上記のコメントで Ramchandra Apte が提案したように、ファイルの最初と最後のバイトを取得することもできます。または、上記のコメントで tdelaney が提案したように、単純にファイル サイズを使用することもできます。ただし、後者の 2 つの戦略はそれぞれ、偽陽性を生成する可能性が高くなります。それらを組み合わせて、偽陽性率を減らすことができます。

非常に多数のファイルを操作している場合は、Bloom Filterのようなより洗練されたデータ構造を使用できます。

于 2013-09-10T16:51:54.427 に答える
3

@senderle には素晴らしい回答がありますが、私のソリューションでは誤検知が発生すると彼が述べたので、私は挑戦が敷かれていると考え、いくつかのコードを示したほうがよいと考えました。私はあなたのmd5関数を細くし(常に「fileSliceLimitation」ケースを使用し、入力バッファでケチを少なくする必要があります)、md5sを実行する前にサイズで事前にフィルタリングしました。

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(10進数のキロバイト)は人に愛され、1024(2進数のキビバイト)はコンピュータやファイルシステムに愛されているからです。私は書くのが習慣some_number*1024なので、1 KiB の増分を参照していることは簡単にわかります。5000000 も妥当な数値ですが、ファイル システムに適切に合わせられるように、5*1024*1024 (つまり 5 MiB) を考慮する必要があります。

2) このビットは正確には何をしますか: size_map = defaultdict(list)

通常の dict オブジェクトに機能を追加する「defaultdict」を作成します。通常の 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:] (引数 1 以降) はコマンド ラインで指定された追加の引数です。sys.argv[1] は、スクリプトを作成したときにスクリプトをテストするための簡単な方法として使用しました。必要に応じて変更する必要があります。

于 2013-09-10T17:23:57.643 に答える
0

最初にやりたいことは、ファイルをループするときに h_md5 をリストに保存することです。何かのようなもの:

h_md5=[]

ディレクトリをループする前に。と

h_md5.append(getFileHashMD5(fullname))

あなたのループの中に。これで、ループで最後に作成したものだけではなく、出力ファイルと比較するハッシュのリストができました。

また、明らかに、現在のコードでは、リスト内の特定のファイル自体のハッシュが見つかるため、毎回各ファイルに対して 1 つの一致が見つかります。したがって、重複を探したい場合は、2 つの異なる一致が見つかったインスタンスを探す必要があります。

編集:コードを変更しても構わないと思っている場合、@senderleの上の答えはこれを行うためのはるかに良い方法です。

于 2013-09-10T16:45:23.700 に答える