私はhashlib(Python 2.6 / 3.0のmd5を置き換えます)を使用しましたが、ファイルを開いてそのコンテンツをhashlib.md5()
関数に配置すると問題なく動作しました。
問題は、ファイルのサイズがRAMサイズを超える可能性がある非常に大きなファイルです。
ファイル全体をメモリに読み込まずにファイルのMD5ハッシュを取得する方法
私はhashlib(Python 2.6 / 3.0のmd5を置き換えます)を使用しましたが、ファイルを開いてそのコンテンツをhashlib.md5()
関数に配置すると問題なく動作しました。
問題は、ファイルのサイズがRAMサイズを超える可能性がある非常に大きなファイルです。
ファイル全体をメモリに読み込まずにファイルのMD5ハッシュを取得する方法
回答:
ファイルを8192バイトのチャンク(または128バイトのその他の倍数)に分割し、を使用してそれらを連続的にMD5にフィードしますupdate()
。
これは、MD5に128バイトのダイジェストブロックがあることを利用しています(8192は128×64)。ファイル全体をメモリに読み込むわけではないので、8192バイトを超えるメモリは使用しません。
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
適切なサイズのチャンクでファイルを読み取る必要があります。
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」ツールを使用して結果をクロスチェックしました。
jacksum -a md5 <filename>
rb
て開く必要があるというopen
ことです。
hexdigest
代わりにdigest
を使用すると、ハッシュのほとんどの例のように「見える」16進数のハッシュが生成されます。
if len(data) < block_size: break
?
open
常に、位置をファイルの先頭に設定して、新しいファイルハンドルを開きます(追加のためにファイルを開く場合を除く)。
以下に、コメントからの提案を組み込みました。ありがとうございます!
import hashlib
def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
h = hash_factory()
with open(filename,'rb') as f:
for chunk in iter(lambda: f.read(chunk_num_blocks*h.block_size), b''):
h.update(chunk)
return h.digest()
import hashlib
def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
h = hash_factory()
with open(filename,'rb') as f:
while chunk := f.read(chunk_num_blocks*h.block_size):
h.update(chunk)
return h.digest()
もっと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 ''( ''だけではない)を返すため、iter()funcは、返されたイテレーターがEOFで停止するために空のバイト文字列を必要とすることに注意してください。
128*md5.block_size
代わりになどを使用してください8192
。
md5.block_size
。
b''
構文は、私には新鮮でした。ここで説明。
これが@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()
そして最後に、
-これはコミュニティによって作成されました。アドバイス/アイデアをありがとうございます。
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()
、センチネル(空の文字列)で関数を使用することです。
この場合に作成されたイテレータは、そのメソッドの呼び出しごとに引数なしでo [ラムダ関数]を呼び出し
next()
ます。返される値がセンチネルと等しい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
Djangoの承認済み回答の実装:
import hashlib
from django.db import models
class MyModel(models.Model):
file = models.FileField() # any field based on django.core.files.File
def get_hash(self):
hash = hashlib.md5()
for chunk in self.file.chunks(chunk_size=8192):
hash.update(chunk)
return hash.hexdigest()
ループは好きではありません。@Nathan Fegerに基づく:
md5 = hashlib.md5()
with open(filename, 'rb') as f:
functools.reduce(lambda _, c: md5.update(c), iter(lambda: f.read(md5.block_size * 128), b''), None)
md5.hexdigest()
hashlib
s APIが他のPythonとうまく連携しないことでした。たとえば、shutil.copyfileobj
どれが密接に機能しないかを考えてみましょう。私の次のアイデアはfold
(別名reduce
)で、イテラブルを1つのオブジェクトにまとめます。例えばハッシュのように。hashlib
これを少し面倒にする演算子を提供していません。それにもかかわらず、ここではイテラブルを折りたたんでいました。
import hashlib,re
opened = open('/home/parrot/pass.txt','r')
opened = open.readlines()
for i in opened:
strip1 = i.strip('\n')
hash_object = hashlib.md5(strip1.encode())
hash2 = hash_object.hexdigest()
print hash2
この辺りで騒ぎが少なすぎないかわかりません。最近、md5とMySQLのblobとして保存されたファイルに問題が発生したため、さまざまなファイルサイズと簡単なPythonのアプローチを試しました。
FileHash=hashlib.md5(FileData).hexdigest()
2Kbから20Mbのファイルサイズの範囲では、顕著なパフォーマンスの違いを検出できなかったため、ハッシュを「チャンク」する必要はありませんでした。とにかく、Linuxがディスクにアクセスする必要がある場合、少なくとも平均的なプログラマーがディスクを使用しないようにする能力と同様に、ディスクを使用する必要があります。偶然にも、問題はmd5とは何の関係もありませんでした。MySQLを使用している場合は、md5()関数とsha1()関数がすでにそこにあることを忘れないでください。