Utiliser hashlib pour calculer md5 digest d'un fichier en Python 3

avec python 2.7 le code suivant calcule l'hexdigest mD5 du contenu d'un fichier.

(EDIT: eh bien, pas vraiment, puisque les réponses ont montré, j'ai juste pensé).

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()

maintenant, si j'exécute ce code en utilisant python3, il relance une Exception TypeError:

    d.update(buf)
TypeError: object supporting the buffer API required

j'ai compris que je pouvais faire tourner ce code avec à la fois python2 et python3 le changeant en:

def md5sum(filename):
    f = open(filename, mode='r')
    d = hashlib.md5()
    for buf in f.read(128):
        d.update(buf.encode())
    return d.hexdigest()

maintenant je me demande encore pourquoi le code original a cessé de fonctionner. Il semble que lors de l'ouverture d'un fichier en utilisant le modificateur de mode binaire, il renvoie des entiers au lieu de chaînes encodées en octets (je dis cela parce que type(buf) renvoie int). Est ce comportement est expliqué quelque part ?

18
demandé sur Tshepang 2011-10-20 03:36:35

3 réponses

je pense que vous vouliez que la for-loop fasse des appels successifs à f.read(128). Qui peut être fait à l'aide de iter () et functools.partielle():

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'))
27
répondu Raymond Hettinger 2011-10-20 02:08:51
for buf in f.read(128):
  d.update(buf)

.. les mises à jour de la table de hachage successivement avec chacun des 128 premiers octets valeurs du fichier. Depuis l'itération sur un bytes produit int objects, vous recevez les appels suivants qui causent l'erreur que vous avez rencontrée dans Python3.

d.update(97)
d.update(98)
d.update(99)
d.update(100)

qui n'est pas ce que vous voulez.

au Lieu de cela, vous voulez:

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
répondu phihag 2011-10-21 07:38:50

j'ai finalement changé mon code pour la version ci-dessous (que je trouve facile à comprendre), après avoir posé la question. Mais je vais probablement le changer pour la version suggérée par Raymond Hetting unsing functools.partiel.

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
répondu kriss 2011-10-20 07:01:52