Pythonでの大きなファイルのMD5チェックサム取得方法
ファイルサイズとかキにせずにチェックサム(ハッシュ値)を取ろうとすれば、
1 2 3 |
|
hashlib
でmd5
形式(sha1
等も使える)のハッシュを作成して
hexdigest
メソッドを使うとこれを16進法形式で返してくれます。
ファイルを読むこむ時にはb
を指定してバイナリ形式で読み込みます。
ただ、これだとf.read()
で全部一気に読み込むので、
ファイルサイズが大きいとメモリが足りなくてMemoryError
を出すことがあります。
この様な場合に備えて、ファイルを一気に読み込むのではなく、 必要分ずつ読み込んでハッシュ値を形成する様にすれば いくらでも大きなファイルのハッシュ値も扱える様になります。
1 2 3 4 5 6 |
|
こんな感じで2048 * hashlib.md5().block_size
毎に読み出して
update
で値を加えてチェックサムを作っていくことが出来ます。
iter
は第一引数で呼び出した物が第二引数と一致しない限り
第一引数を返すメソッドですが、
第二引数に単にもう何も無いよ、と''
を入れるのではなく、
b''
としてるのはこの文字をbytes literalとして認識させるため。
ですが、これはPython3で必要ですがPython2ではb
を付けなくても同じです。
(b
を付けても無視される。)1
block_size
はハッシュを計算する時に使う一つ際のブロックサイズなので、
読み込む時にはこの整数倍毎に読み込んでやれば、
全体を一気に読み込む時と同じ値を作ることが出来ます。
Macや手元のLinuxではこの値は64byteになっていました。
読み込む時に余り小さい値にしてしまうと、
細かく分けて読み込むのに時間がかかってしまうので適度に大きな値にします。
(ここでは2048*64 = 131072 byte = 128 kB
.)
ちょっと10GBのファイルでテストしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
こんな感じのテストスクリプトを書いて、dd
で10GBのファイルを作ってテストしてみます。
$ dd if=/dev/zero of=test_file bs=1048576 count=10240
10240+0 records in
10240+0 records out
10737418240 bytes transferred in 16.104331 secs (666741029 bytes/sec)
$ du -h test_file
10.0G test_file
$ time md5sum test_file
2dd26c4d4799ebd29fa31e48d49e8e53 test_file
real 0m24.139s
user 0m19.463s
sys 0m3.994s
(-_-) $ ./md5test.py
2dd26c4d4799ebd29fa31e48d49e8e53
All: 37.094055
2dd26c4d4799ebd29fa31e48d49e8e53
1 * 64: 223.921650
2dd26c4d4799ebd29fa31e48d49e8e53
16 * 64: 36.051889
2dd26c4d4799ebd29fa31e48d49e8e53
124 * 64: 23.426525
2dd26c4d4799ebd29fa31e48d49e8e53
256 * 64: 21.920792
2dd26c4d4799ebd29fa31e48d49e8e53
512 * 64: 21.080897
2dd26c4d4799ebd29fa31e48d49e8e53
1024 * 64: 20.728919
2dd26c4d4799ebd29fa31e48d49e8e53
2048 * 64: 20.639481
2dd26c4d4799ebd29fa31e48d49e8e53
4096 * 64: 20.590142
2dd26c4d4799ebd29fa31e48d49e8e53
8192 * 64: 20.617627
2dd26c4d4799ebd29fa31e48d49e8e53
16384 * 64: 20.696506
一応毎回チェックサムの結果を出してますが、全部同じになっています。
コマンドラインからmd5sum
を使って計算すると
24秒程度ですが、
Pythonで全て一気に読み込むと37秒かかりました。
(16GBのメモリマシンですがMemoryError
なしで読み込めました。)
また、最小単位の64byte毎に読み込むと224秒と大分時間がかかります。
徐々に短縮されていって4096*64=262144 byte = 256 kB
の時が
一番速く20.6秒で終えています。
その後はサイズを大きくしていっても徐々に逆に遅くなる感じ。
勿論環境によりますが、数GB~10数GB程度のメモリだとして、
大体1kB ~ 1MB位のサイズで読みこめば
十分速く読み込め、かつMemoryError
も出ない感じです。
一方、md5sum
コマンドも大きなファイルもきちんと扱ってくれるし
十分速いので、
Pythonの中でも
import commands
print commands.getoutput('md5sum %s' % filename)
と呼んでしまうのも一つの手かもしれません。
(ただし、md5sum
コマンドが無い環境は結構あるので注意。)
参考:
MacでMD5チェックサム
MD5のついで。
ちょっとチェックしようと思ってMacで作業してたら
Macにはmd5sum
コマンドが入っていませんでした。
sha
ハッシュを調べるshasum
コマンドは入っています。
Homebrewでチェックしてみるとmd5sha1sum
というのがあったのでこれを入れてみると、
md5sum
コマンドが入りました。
ついでにsha1sum
というsha1
専用のコマンドも入りました。
(sha256sum
とかはHomebrewには無さそう?)
MD5チェックサムを調べるには
$ md5sum test_file
SHA1チェックサムなら
$ sha1sum test_file
または
$ shasum -a 1 test_file
SHA256なら
$ shasum -a 256 test_file