web-dev-qa-db-ja.com

hashlibを使用してPython 3のファイルのmd5ダイジェストを計算する

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を返すためです)。この動作はどこかで説明されていますか?

23
kriss

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'))
37
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()
10
phihag

質問をした後、ようやくコードを以下のバージョン(わかりやすい)に変更しました。しかし、私はおそらくそれを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()
1
kriss