Hashlib(Python 2.6/3.0のmd5を置き換える)を使用しましたが、ファイルを開いてその内容を hashlib.md5()
関数に入れると正常に機能しました。
問題は、サイズがRAMサイズを超える可能性がある非常に大きなファイルの場合です。
ファイル全体をメモリにロードせずにファイルのMD5ハッシュを取得する方法は?
ファイルを128バイトのチャンクに分割し、update()
を使用して連続してMD5にフィードします。
これは、MD5に128バイトのダイジェストブロックがあるという事実を利用しています。基本的に、MD5がdigest()
sファイルの場合、これはまさにそれが行っていることです。
各反復でメモリを解放する(つまり、ファイル全体をメモリに読み取らない)ことを確認する場合、これには128バイト以下のメモリが必要です。
1つの例は、次のようにチャンクを読み取ることです。
f = open(fileName)
while not endOfFile:
f.read(128)
適切なサイズのチャンクでファイルを読み取る必要があります。
def md5_for_file(f, block_size=2**20):
md5 = hashlib.md5()
while True:
data = f.read(block_size)
if not data:
break
md5.update(data)
return md5.digest()
注:必ず「rb」を指定してファイルを開いてください。そうしないと、間違った結果が得られます。
したがって、1つの方法ですべてを行うには、次のようなものを使用します。
def generate_file_md5(rootdir, filename, blocksize=2**20):
m = hashlib.md5()
with open( os.path.join(rootdir, filename) , "rb" ) as f:
while True:
buf = f.read(blocksize)
if not buf:
break
m.update( buf )
return m.hexdigest()
上記の更新はFrerich Raabeによって提供されたコメントに基づいていました-これをテストし、Python 2.7.2 Windowsインストールで正しいことがわかりました
「ジャックサム」ツールを使用して結果をクロスチェックしました。
jacksum -a md5 <filename>
ファイルを読むためのよりPythonicな(「while True」ではない)方法が必要な場合は、次のコードを確認してください。
import hashlib
def checksum_md5(filename):
md5 = hashlib.md5()
with open(filename,'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
md5.update(chunk)
return md5.digest()
Read()が ''だけでなく)b ''を返すため、返された反復子がEOFで停止するためには、iter()funcが空のバイト文字列を必要とすることに注意してください。
@Piotr Czaplaのメソッドの私のバージョンは次のとおりです。
def md5sum(filename):
md5 = hashlib.md5()
with open(filename, 'rb') as f:
for chunk in iter(lambda: f.read(128 * md5.block_size), b''):
md5.update(chunk)
return md5.hexdigest()
このスレッドで複数のコメント/回答を使用して、ここに私の解決策があります:
import hashlib
def md5_for_file(path, block_size=256*128, hr=False):
'''
Block size directly depends on the block size of your filesystem
to avoid performances issues
Here I have blocks of 4096 octets (Default NTFS)
'''
md5 = hashlib.md5()
with open(path,'rb') as f:
for chunk in iter(lambda: f.read(block_size), b''):
md5.update(chunk)
if hr:
return md5.hexdigest()
return md5.digest()
そして最後に、
-これはコミュニティによって構築されました。アドバイス/アイデアに感謝します。
A Python 2/3ポータブルソリューション
チェックサム(md5、sha1など)を計算するには、バイト値を合計するため、ファイルをバイナリモードで開く必要があります。
Py27/py3ポータブルにするには、次のようにio
パッケージを使用する必要があります。
import hashlib
import io
def md5sum(src):
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
content = fd.read()
md5.update(content)
return md5
ファイルが大きい場合は、ファイルの内容全体をメモリに保存しないように、ファイルをチャンクで読み取ることをお勧めします。
def md5sum(src, length=io.DEFAULT_BUFFER_SIZE):
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
for chunk in iter(lambda: fd.read(length), b''):
md5.update(chunk)
return md5
ここでのコツは、 iter()
関数をsentinel(空の文字列)とともに使用することです。
この場合に作成される反復子は、
next()
メソッドの呼び出しごとに引数なしでo[ラムダ関数]を呼び出します。返される値がセンチネルに等しい場合、StopIteration
が発生します。それ以外の場合、値が返されます。
ファイルが本当に大きい場合は、進捗情報も表示する必要があります。これを行うには、計算されたバイト数を出力または記録するコールバック関数を呼び出します。
def md5sum(src, callback, length=io.DEFAULT_BUFFER_SIZE):
calculated = 0
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
for chunk in iter(lambda: fd.read(length), b''):
md5.update(chunk)
calculated += len(chunk)
callback(calculated)
return md5
一般的なハッシュ関数に関するHawkwingのコメントを考慮に入れたBastien Semeneコードのリミックス...
def hash_for_file(path, algorithm=hashlib.algorithms[0], block_size=256*128, human_readable=True):
"""
Block size directly depends on the block size of your filesystem
to avoid performances issues
Here I have blocks of 4096 octets (Default NTFS)
Linux Ext4 block size
Sudo tune2fs -l /dev/sda5 | grep -i 'block size'
> Block size: 4096
Input:
path: a path
algorithm: an algorithm in hashlib.algorithms
ATM: ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
block_size: a multiple of 128 corresponding to the block size of your filesystem
human_readable: switch between digest() or hexdigest() output, default hexdigest()
Output:
hash
"""
if algorithm not in hashlib.algorithms:
raise NameError('The algorithm "{algorithm}" you specified is '
'not a member of "hashlib.algorithms"'.format(algorithm=algorithm))
hash_algo = hashlib.new(algorithm) # According to hashlib documentation using new()
# will be slower then calling using named
# constructors, ex.: hashlib.md5()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(block_size), b''):
hash_algo.update(chunk)
if human_readable:
file_hash = hash_algo.hexdigest()
else:
file_hash = hash_algo.digest()
return file_hash
完全なコンテンツを読まないとmd5を取得できません。しかし、uは pdate 関数を使用して、ブロックごとにファイルコンテンツを読み取ることができます。
m.update(a); m.update(b)はm.update(a + b)と同等です
次のコードはもっとPythonicだと思います:
from hashlib import md5
def get_md5(fname):
m = md5()
with open(fname, 'rb') as fp:
for chunk in fp:
m.update(chunk)
return m.hexdigest()