python 2.7の場合、次のコードはファイルのコンテンツのmD5 hexdigestを計算します。
(編集:まあ、答えが示しているように実際にはそうではありません、私はちょうどそう思った)。
import hashlib
def md5sum(filename):
f = open(filename, mode='rb')
d = hashlib.md5()
for buf in f.read(128):
d.update(buf)
return d.hexdigest()
Python3を使用してそのコードを実行すると、TypeError例外が発生します。
d.update(buf)
TypeError: object supporting the buffer API required
Python2とpython3の両方でコードを実行して、次のように変更できることがわかりました。
def md5sum(filename):
f = open(filename, mode='r')
d = hashlib.md5()
for buf in f.read(128):
d.update(buf.encode())
return d.hexdigest()
今でも、なぜ元のコードが機能しなくなったのか疑問に思います。バイナリモード修飾子を使用してファイルを開くと、バイトとしてエンコードされた文字列ではなく整数が返されるようです(type(buf)はintを返すためです)。この動作はどこかで説明されていますか?
Forループでf.read(128)
を連続して呼び出す必要があると思います。 iter()およびfunctools.partial()を使用してそれを行うことができます:
import hashlib
from functools import partial
def md5sum(filename):
with open(filename, mode='rb') as f:
d = hashlib.md5()
for buf in iter(partial(f.read, 128), b''):
d.update(buf)
return d.hexdigest()
print(md5sum('utils.py'))
for buf in f.read(128):
d.update(buf)
..ファイルの最初の128 bytes値のそれぞれでハッシュを順番に更新します。 bytes
を反復処理するとint
オブジェクトが生成されるため、次の呼び出しが発生し、Python3で発生したエラーが発生します。
d.update(97)
d.update(98)
d.update(99)
d.update(100)
それはあなたが望むものではありません。
代わりに、次のようにします。
def md5sum(filename):
with open(filename, mode='rb') as f:
d = hashlib.md5()
while True:
buf = f.read(4096) # 128 is smaller than the typical filesystem block
if not buf:
break
d.update(buf)
return d.hexdigest()
質問をした後、ようやくコードを以下のバージョン(わかりやすい)に変更しました。しかし、私はおそらくそれをRaymondHettingがfunctools.partialを歌っていないことによって提案されたバージョンに変更するでしょう。
import hashlib
def chunks(filename, chunksize):
f = open(filename, mode='rb')
buf = "Let's go"
while len(buf):
buf = f.read(chunksize)
yield buf
def md5sum(filename):
d = hashlib.md5()
for buf in chunks(filename, 128):
d.update(buf)
return d.hexdigest()