Python でファイルのリストの MD5 チェックサムを生成 (およびチェック) する簡単な方法はありますか? (私が取り組んでいる小さなプログラムがあり、ファイルのチェックサムを確認したいのですが)。
6 に答える
hashlib.md5()を使用できます
ファイル全体をメモリに収めることができない場合があることに注意してください。その場合、4096 バイトのチャンクを順番に読み取り、md5
メソッドにフィードする必要があります。
import hashlib
def md5(fname):
hash_md5 = hashlib.md5()
with open(fname, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
注: パックされたバイトを使用するだけでよい場合は、ダイジェストの16 進文字列hash_md5.hexdigest()
表現が返されるため、元に戻す必要はありません。return hash_md5.digest()
かなりメモリ効率の悪い方法があります。
単一ファイル:
import hashlib
def file_as_bytes(file):
with file:
return file.read()
print hashlib.md5(file_as_bytes(open(full_path, 'rb'))).hexdigest()
ファイルのリスト:
[(fname, hashlib.md5(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]
ただし、MD5 は壊れていることが知られており、脆弱性分析は非常に難しいため、いかなる目的にも使用すべきではないことを思い出してください。また、セキュリティ問題のためにコードが将来使用される可能性を分析することは不可能です。IMHO、それを使用するすべての人が更新を余儀なくされるように、ライブラリから完全に削除する必要があります。したがって、代わりに次のことを行う必要があります。
[(fname, hashlib.sha256(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]
128 ビット分のダイジェストのみが必要な場合は、.digest()[:16]
.
これにより、タプルのリストが得られます。各タプルには、ファイルの名前とそのハッシュが含まれています。
繰り返しになりますが、あなたの MD5 の使用について強く質問します。少なくとも SHA1 を使用する必要があり、最近 SHA1 で発見された欠陥を考えると、おそらくそれさえありません。「暗号化」の目的で MD5 を使用していない限り、問題ないと考える人もいます。しかし、物事は当初の予想よりも範囲が広くなる傾向があり、何気ない脆弱性分析が完全に間違っていることが判明する可能性があります。最初から正しいアルゴリズムを使用する習慣を身につけるのが最善です。別の文字の束を入力するだけです。それほど難しくありません。
より複雑ですが、メモリ効率の良い方法を次に示します。
import hashlib
def hash_bytestr_iter(bytesiter, hasher, ashexstr=False):
for block in bytesiter:
hasher.update(block)
return hasher.hexdigest() if ashexstr else hasher.digest()
def file_as_blockiter(afile, blocksize=65536):
with afile:
block = afile.read(blocksize)
while len(block) > 0:
yield block
block = afile.read(blocksize)
[(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.md5()))
for fname in fnamelst]
そして、繰り返しになりますが、MD5 は壊れているため、実際には使用されるべきではありません。
[(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.sha256()))
for fname in fnamelst]
繰り返しますが、128 ビット分のダイジェストのみが必要な場合[:16]
は、呼び出しの後に置くことができます。hash_bytestr_iter(...)
私は明らかに根本的に新しいものを追加していませんが、ステータスをコメントする前にこの回答を追加しました。さらに、コード領域により、物事がより明確になります-とにかく、特に Omnifarious の回答からの @Nemo の質問に回答するために:
私はたまたまチェックサムについて少し考えていました (具体的には、ブロック サイズに関する提案を探してここに来ました)、この方法は予想よりも高速である可能性があることがわかりました。最速の(しかしかなり典型的な)timeit.timeit
または/usr/bin/time
約のファイルをチェックサムするいくつかの方法のそれぞれから結果を取得します。11MB:
$ ./sum_methods.py
crc32_mmap(filename) 0.0241742134094
crc32_read(filename) 0.0219960212708
subprocess.check_output(['cksum', filename]) 0.0553209781647
md5sum_mmap(filename) 0.0286180973053
md5sum_read(filename) 0.0311000347137
subprocess.check_output(['md5sum', filename]) 0.0332629680634
$ time md5sum /tmp/test.data.300k
d3fe3d5d4c2460b5daacc30c6efbc77f /tmp/test.data.300k
real 0m0.043s
user 0m0.032s
sys 0m0.010s
$ stat -c '%s' /tmp/test.data.300k
11890400
したがって、Python と /usr/bin/md5sum の両方で、11MB のファイルに約 30ms かかるようです。関連するmd5sum
関数 (md5sum_read
上記のリスト) は、Omnifarious のものとかなり似ています。
import hashlib
def md5sum(filename, blocksize=65536):
hash = hashlib.md5()
with open(filename, "rb") as f:
for block in iter(lambda: f.read(blocksize), b""):
hash.update(block)
return hash.hexdigest()
確かに、これらは単一の実行からのmmap
ものであり (少なくとも数十回の実行が行われると、それらは常にわずかに高速になります)、私のものは通常f.read(blocksize)
、バッファーが使い果たされた後に余分なものを取得しますが、合理的に繰り返し可能でありmd5sum
、コマンドラインで必ずしも Python の実装よりも高速というわけではありません...
編集:長い間遅れて申し訳ありませんが、しばらくこれを見ていませんでしたが、@ EdRandallの質問に答えるために、Adler32の実装を書き留めます。ただし、ベンチマークは実行していません。基本的には CRC32 と同じです: init、update、digest 呼び出しの代わりに、すべてがzlib.adler32()
呼び出しです:
import zlib
def adler32sum(filename, blocksize=65536):
checksum = zlib.adler32("")
with open(filename, "rb") as f:
for block in iter(lambda: f.read(blocksize), b""):
checksum = zlib.adler32(block, checksum)
return checksum & 0xffffffff
これは空の文字列で開始する必要があることに注意してください。アドラーの合計は、ゼロから開始する場合と の合計で実際に異なるためです""
。つまり、1
CRC は0
代わりに開始できます。AND
-ing は、32 ビットの符号なし整数にするために必要です。これにより、Python バージョン間で同じ値が返されることが保証されます。