45 votes

Pourquoi le calcul du hachage Python "hashlib.sha1" est-il différent de "git hash-object" pour un fichier ?

Je suis en train de calculer la valeur SHA-1 d'un fichier.

J'ai fabriqué ce script:

def hashfile(filepath):
    sha1 = hashlib.sha1()
    f = open(filepath, 'rb')
    try:
        sha1.update(f.read())
    finally:
        f.close()
    return sha1.hexdigest()

Pour un fichier spécifique, j'obtiens cette valeur de hachage:
8c3e109ff260f7b11087974ef7bcdbdc69a0a3b9
Mais lorsque je calcule la valeur avec git hash_object, alors j'obtiens cette valeur: d339346ca154f6ed9e92205c3c5c38112e761eb7

Pourquoi sont-elles différentes? Est-ce que je fais quelque chose de mal, ou puis-je simplement ignorer la différence?

51voto

Brian R. Bondy Points 141769

Git calcule les hashs comme ceci :

sha1("blob " + taille_fichier + "\0" + données)

Référence

32voto

Ben Points 635

Pour référence, voici une version plus concise:

def sha1OfFile(filepath):
    import hashlib
    with open(filepath, 'rb') as f:
        return hashlib.sha1(f.read()).hexdigest()

Après réflexion : bien que je ne l'aie jamais vu, je pense qu'il y a un potentiel pour que f.read() renvoie moins que le fichier complet, ou pour un fichier de plusieurs gigaoctets, que f.read() manque de mémoire. Pour l'édification de tous, réfléchissons à la façon de corriger cela: Une première correction serait :

def sha1OfFile(filepath):
    import hashlib
    sha = hashlib.sha1()
    with open(filepath, 'rb') as f:
        for line in f:
            sha.update(line)
        return sha.hexdigest()

Cependant, rien ne garantit que '\n' apparaisse dans le fichier, donc le fait que la boucle for nous donne des blocs de fichier se finissant par '\n' pourrait nous poser le même problème qu'au départ. Malheureusement, je ne vois pas de moyen aussi Pythonique d'itérer sur des blocs de fichier aussi volumineux que possible, ce qui signifie, je pense, que nous sommes bloqués avec une boucle while True : ... break et avec un nombre magique pour la taille du bloc :

def sha1OfFile(filepath):
    import hashlib
    sha = hashlib.sha1()
    with open(filepath, 'rb') as f:
        while True:
            block = f.read(2**10) # Nombre magique : blocs d'un mégaoctet.
            if not block: break
            sha.update(block)
        return sha.hexdigest()

Bien sûr, qui pourrait dire que nous pouvons stocker des chaînes d'un mégaoctet. Nous le pouvons probablement, mais que se passe-t-il si nous sommes sur un petit ordinateur embarqué ?

J'aimerais pouvoir penser à une manière plus propre garantie de ne pas manquer de mémoire sur des fichiers énormes et qui n'a pas de nombres magiques et qui fonctionne aussi bien que la solution simple Pythonique originale.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X