ファイルのMD5チェックサムを生成する


348

PythonでファイルのリストのMD5チェックサムを生成(およびチェック)する簡単な方法はありますか?(私が取り組んでいる小さなプログラムがあり、ファイルのチェックサムを確認したいと思います)。


3
なぜ使用しないのmd5sumですか?
kennytm 2010

99
これをPythonで維持すると、クロスプラットフォーム互換性の管理が容易になります。
アレクサンダー

あなたは(非常に大きなファイルの場合)、「プログレスバー*または類似とデ・ソリューションをしたい場合は、このソリューションを検討してください:stackoverflow.com/questions/1131220/...
ローランLAPORTE

1
@kennytmあなたが提供したリンクは、2番目の段落でこれを述べています:「基礎となるMD5アルゴリズムはもはや安全であるとは見なされていません」を説明している間md5sum。セキュリティを意識したプログラマが私の意見ではそれを使用すべきではないのはそのためです。
Debug255 2018

1
@ Debug255良い点と有効な点。どちらもmd5sumこのSOの質問に記載された技術では避けるべきである-それが可能ならば、SHA-2またはSHA-3使用することをお勧めします:en.wikipedia.org/wiki/Secure_Hash_Algorithms
パーLundbergの

回答:


462

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()

注: パックされたバイトuseだけが必要な場合は、ダイジェストの16進文字列表現をhash_md5.hexdigest()返すため、元に戻す必要はありません。return hash_md5.digest()


297

かなりメモリ効率の悪い方法があります。

単一ファイル:

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は壊れていることがわかっているため、脆弱性の分析は非常に難しいため、目的に使用しないでください。また、セキュリティの問題のためにコードを将来使用する可能性を分析することは不可能です。私見、ライブラリから完全に削除する必要があるので、それを使用するすべての人は更新を余儀なくされます。だから、代わりにあなたがすべきことはここにあります:

[(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(...)ます。


66
MD5を使用して、ファイルが破損していないことを確認しています。壊れることはあまり気にしていません。
アレクサンダー

87
@TheLifelessOne:そして、@ Omnifarious恐ろしい警告にもかかわらず、それはMD5の完全に良い使い方です。
James K. Polk大統領

22
@ GregS、@ TheLifelessOne-ええ、そしてあなたが知っている次のことは、あなたがアプリケーションについてこの事実を使用して、ファイルがまったく予期していないファイルである場合に、ファイルが破損していないものとして受け入れられるようにする方法を見つけたということです。いいえ、私は私の怖い警告に立ち向かいます。MD5は削除するか、非推奨の警告を表示する必要があると思います。
10

10
おそらく、.digest()の代わりに.hexdigest()を使用します-OPの目的は、人間が読みやすくすることです。
zbstof 2012

21
私はこのソリューションを使用しましたが、2つの異なるPDFファイルに同じハッシュを誤って与えました。解決策は、バイナリモードを指定してファイルを開くことでした。つまり、[(fname、hashlib.md5(open(fname、'rb').read())。hexdigest())for fname in fnamelst]これはより関連していますmd5よりもopen関数に追加しましたが、上記のクロスプラットフォーム互換性の要件があるため、それを報告することは有用であると考えました(参照:docs.python.org/2/tutorial/…)。
BlueCoder 2013

34

根本的に新しいものは何も追加していないことは明らかですが、コメントステータスになる前にこの回答を追加しました。さらに、コード領域によって状況がより明確になります。特に、特に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

これは空の文字列で始まる必要があることに注意してください。Adlerの合計は、ゼロからの開始と実際のの合計が実際に異なるためです""。つまり、1CRCは0代わりに開始できます。AND-ingは、それがPythonのバージョン間で同じ値を返す保証する32ビット符号なし整数にするために必要とされます。


SHA1とzlib.adler32を比較する数行を追加することはできますか?
Ed Randall

1
上記のmd5sum()関数は、ファイルへの書き込みアクセス権があることを前提としています。open()コールの「r + b」を「rb」に置き換えると、正常に機能します。
Kevin Lyda

1
@EdRandall:たとえば、adler32は気にする価値がありません。leviathansecurity.com/blog/analysis-of-adler32
MikeW

6

Python 3.8以降では、次のことができます

import hashlib
with open("your_filename.txt", "rb") as f:
    file_hash = hashlib.md5()
    while chunk := f.read(8192):
        file_hash.update(chunk)

print(file_hash.digest())
print(file_hash.hexdigest())  # to get a printable str instead of bytes

使用を検討してhashlib.blake2bの代わりに、md5(単に置き換えるmd5blake2b上記のスニペットで)。暗号的に安全で、MD5 より高速です。


:=オペレータは(Pythonの3.8+に新しい)「代入演算子」です。より大きな式の内部に値を割り当てることができます。詳細はこちら:docs.python.org/3/whatsnew/3.8.html#assignment-expressions
ベンジャミン

0
hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()

3
こんにちは!これが問題の解決策である理由について、コードに説明を追加してください。さらに、この投稿はかなり古いので、ソリューションが他の人がまだ対処していないものを追加する理由についての情報も追加する必要があります。
d_kennetz

1
それは別のメモリ非効率的な方法です
Erik Aronesty

-2

呼び出しパッケージとmd5sumバイナリに依存する方が、サブプロセスやmd5パッケージよりも少し便利だと思います

import invoke

def get_file_hash(path):

    return invoke.Context().run("md5sum {}".format(path), hide=True).stdout.split(" ")[0]

もちろん、これは、invokeとmd5sumがインストールされていることを前提としています。


3
pathがユーザー指定のパスの場合、これにより、ユーザーはシステム上で任意のbashコマンドを実行できます。
ボリス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.