Использование hashlib для вычисления дайджеста md5 файла в Python 3 - PullRequest
21 голосов
/ 20 октября 2011

В python 2.7 следующий код вычисляет шестнадцатеричный mD5-контент содержимого файла.

(РЕДАКТИРОВАТЬ: ну, не совсем так, как показали ответы, я просто так думал).

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). Это поведение где-то объяснено?

Ответы [ 3 ]

29 голосов
/ 20 октября 2011

Я думаю, вы хотели, чтобы цикл 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'))
10 голосов
/ 20 октября 2011
for buf in f.read(128):
  d.update(buf)

.. обновляет хэш последовательно с каждым из первых 128 байтов значений файла. Поскольку итерация по 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()
1 голос
/ 20 октября 2011

Я наконец изменил свой код на версию ниже (которую я нахожу легкой для понимания) после того, как задал вопрос.Но я, вероятно, изменю его на версию, предложенную Рэймондом Хеттингом без указания 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()
...