Hachage d'un fichier en Python


98

Je veux que python lise l'EOF afin que je puisse obtenir un hachage approprié, que ce soit sha1 ou md5. Veuillez aider. Voici ce que j'ai jusqu'à présent:

import hashlib

inputFile = raw_input("Enter the name of the file:")
openedFile = open(inputFile)
readFile = openedFile.read()

md5Hash = hashlib.md5(readFile)
md5Hashed = md5Hash.hexdigest()

sha1Hash = hashlib.sha1(readFile)
sha1Hashed = sha1Hash.hexdigest()

print "File Name: %s" % inputFile
print "MD5: %r" % md5Hashed
print "SHA1: %r" % sha1Hashed

6
et quel est le problème?
isedev

1
Je veux qu'il puisse hacher un fichier. J'en ai besoin pour lire jusqu'à l'EOF, quelle que soit la taille du fichier.
user3358300

3
c'est exactement ce que file.read()fait - lire le fichier entier.
isedev

La documentation de la read()méthode dit?
Ignacio Vazquez-Abrams

Vous devriez passer par "qu'est-ce que le hachage?".
Sharif Mamun

Réponses:


135

TL; DR utilise des tampons pour ne pas utiliser des tonnes de mémoire.

Nous arrivons au cœur de votre problème, je crois, lorsque nous considérons les implications mémoire du travail avec de très gros fichiers . Nous ne voulons pas que ce mauvais garçon produise 2 Go de RAM pour un fichier de 2 Go , donc, comme le souligne pasztorpisti , nous devons traiter ces fichiers plus gros par morceaux!

import sys
import hashlib

# BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536  # lets read stuff in 64kb chunks!

md5 = hashlib.md5()
sha1 = hashlib.sha1()

with open(sys.argv[1], 'rb') as f:
    while True:
        data = f.read(BUF_SIZE)
        if not data:
            break
        md5.update(data)
        sha1.update(data)

print("MD5: {0}".format(md5.hexdigest()))
print("SHA1: {0}".format(sha1.hexdigest()))

Ce que nous avons fait, c'est que nous mettons à jour nos hachages de ce mauvais garçon en morceaux de 64 Ko au fur et à mesure que nous suivons la méthode pratique de mise à jour dandy de hashlib . De cette façon, nous utilisons beaucoup moins de mémoire que les 2 Go qu'il faudrait pour hacher le gars en même temps!

Vous pouvez tester cela avec:

$ mkfile 2g bigfile
$ python hashes.py bigfile
MD5: a981130cf2b7e09f4686dc273cf7187e
SHA1: 91d50642dd930e9542c39d36f0516d45f4e1af0d
$ md5 bigfile
MD5 (bigfile) = a981130cf2b7e09f4686dc273cf7187e
$ shasum bigfile
91d50642dd930e9542c39d36f0516d45f4e1af0d  bigfile

J'espère que cela pourra aider!

Tout cela est également décrit dans la question liée sur le côté droit: Obtenez le hachage MD5 de gros fichiers en Python


Addenda!

En général, lors de l'écriture de python, il est utile de prendre l'habitude de suivre pep-8 . Par exemple, en python, les variables sont généralement séparées par des traits de soulignement et non par camelCased. Mais ce n'est que du style et personne ne se soucie vraiment de ces choses, sauf les gens qui doivent lire le mauvais style ... ce qui pourrait être vous en lisant ce code dans des années.


@ranman Bonjour, je n'ai pas pu obtenir la partie {0} ". format (sha1.hexdigest ()). Pourquoi l'utilisons-nous au lieu d'utiliser simplement sha1.hexdigest ()?
Belial

@Belial Qu'est-ce qui ne fonctionnait pas? J'utilisais principalement cela pour différencier les deux hachages ...
Randall Hunt

@ranman Tout fonctionne, je n'ai jamais utilisé cela et je ne l'ai pas vu dans la littérature. "{0}". Format () ... inconnu de moi. :)
Belial

1
Comment dois-je choisir BUF_SIZE?
Martin Thoma

1
Cela ne génère pas les mêmes résultats que les shasumbinaires. L'autre réponse répertoriée ci-dessous (celle utilisant memoryview) est compatible avec d'autres outils de hachage.
tedivm

61

Pour le calcul correct et efficace de la valeur de hachage d'un fichier (en Python 3):

  • Ouvrez le fichier en mode binaire (c.-à-d. 'b' à- au mode fichier) pour éviter les problèmes de codage de caractères et de conversion de fin de ligne.
  • Ne lisez pas le fichier complet en mémoire, car c'est une perte de mémoire. Au lieu de cela, lisez-le séquentiellement bloc par bloc et mettez à jour le hachage pour chaque bloc.
  • Éliminez le double tampon, c'est-à-dire n'utilisez pas d'E / S tamponnées, car nous utilisons déjà une taille de bloc optimale.
  • Utilisez readinto()pour éviter le barattage du tampon.

Exemple:

import hashlib

def sha256sum(filename):
    h  = hashlib.sha256()
    b  = bytearray(128*1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        for n in iter(lambda : f.readinto(mv), 0):
            h.update(mv[:n])
    return h.hexdigest()

2
Comment savoir ce qu'est une taille de bloc optimale?
Mitar

1
@Mitar, une limite inférieure est le maximum du bloc physique (traditionnellement 512 octets ou 4 Ko avec les disques plus récents) et la taille de page des systèmes (4 Ko sur de nombreux systèmes, autres choix courants: 8 Ko et 64 Ko). Ensuite, vous effectuez essentiellement un benchmarking et / ou regardez les résultats de benchmark publiés et les travaux associés (par exemple, vérifiez ce que rsync / GNU cp / ... utilise actuellement).
maxschlepzig

Serait- resource.getpagesizeil utile ici, si nous voulions essayer de l'optimiser quelque peu dynamiquement? Et qu'en est-il mmap?
jpmc26

@ jpmc26, getpagesize () n'est pas si utile ici - les valeurs courantes sont 4 Kio ou 8 Kio, quelque chose dans cette plage, c'est-à-dire quelque chose de beaucoup plus petit que 128 Kio - 128 Kio est généralement un bon choix. mmap n'aide pas beaucoup dans notre cas d'utilisation car nous lisons séquentiellement le fichier complet de l'avant vers l'arrière. mmap présente des avantages lorsque le modèle d'accès est plus aléatoire, si les pages sont accédées plus d'une fois et / ou si cela simplifie la gestion du tampon de lecture.
maxschlepzig

3
J'ai comparé à la fois la solution de (1) @Randall Hunt et (2) la vôtre (dans cet ordre, est important en raison du cache de fichiers) avec un fichier d'environ 116 Go et l'algorithme sha1sum. La solution 1 a été modifiée afin d'utiliser un tampon de 20 * 4096 (PAGE_SIZE) et définir le paramètre de mise en tampon à 0. L'algorithme de la solution 2 uniquement a été modifié (sha256 -> sha1). Résultat: (1) 3m37.137s (2) 3m30.003s. Le sha1sum natif en mode binaire: 3m31.395s
bioinfornatics

18

Je proposerais simplement:

def get_digest(file_path):
    h = hashlib.sha256()

    with open(file_path, 'rb') as file:
        while True:
            # Reading is buffered, so we can read smaller chunks.
            chunk = file.read(h.block_size)
            if not chunk:
                break
            h.update(chunk)

    return h.hexdigest()

Toutes les autres réponses ici semblent trop compliquer. Python met déjà en mémoire tampon lors de la lecture (de manière idéale, ou vous configurez cette mise en mémoire tampon si vous avez plus d'informations sur le stockage sous-jacent) et il est donc préférable de lire par morceaux que la fonction de hachage trouve idéale, ce qui la rend plus rapide ou au moins moins gourmande en CPU pour calculer la fonction de hachage. Ainsi, au lieu de désactiver la mise en mémoire tampon et d'essayer de l'émuler vous-même, vous utilisez la mise en mémoire tampon Python et contrôlez ce que vous devriez contrôler: ce que le consommateur de vos données trouve idéal, la taille du bloc de hachage.


Réponse parfaite, mais ce serait bien si vous souteniez vos déclarations avec la documentation associée: Python3 - open () et Python2 - open () . Même en tenant compte des différences entre les deux, l'approche de Python3 est plus sophistiquée. Néanmoins, j'ai vraiment apprécié la perspective centrée sur le consommateur!
Murmel le

hash.block_sizeest documentée comme la «taille de bloc interne de l'algorithme de hachage». Hashlib ne le trouve pas idéal . Rien dans la documentation du package ne suggère de update()préférer hash.block_sizeune entrée de taille. Il n'utilise pas moins de CPU si vous l'appelez comme ça. Votre file.read()appel entraîne de nombreuses créations d'objets inutiles et des copies superflues du tampon de fichier vers votre nouvel objet octets de bloc.
maxschlepzig

Les hachages mettent à jour leur état par block_sizeblocs. Si vous ne les fournissez pas dans ces morceaux, ils doivent mettre en mémoire tampon et attendre que suffisamment de données apparaissent, ou diviser les données données en morceaux en interne. Ainsi, vous pouvez simplement gérer cela de l'extérieur, puis simplifier ce qui se passe en interne. Je trouve cet idéal. Voir par exemple: stackoverflow.com/a/51335622/252025
Mitar

Le block_sizeest beaucoup plus petit que toute taille de lecture utile. De plus, tous les blocs et tailles de lecture utiles sont des puissances de deux. Ainsi, la taille de lecture est divisible par la taille de bloc pour toutes les lectures sauf éventuellement la dernière. Par exemple, la taille du bloc sha256 est de 64 octets. Cela signifie qu'il update()est capable de traiter directement l'entrée sans aucune mise en mémoire tampon jusqu'à un multiple de block_size. Ainsi, seulement si la dernière lecture n'est pas divisible par la taille du bloc, elle doit mettre en mémoire tampon jusqu'à 63 octets, une fois. Par conséquent, votre dernier commentaire est incorrect et ne soutient pas les affirmations que vous faites dans votre réponse.
maxschlepzig

Le fait est qu'il n'est pas nécessaire d'optimiser la mise en mémoire tampon car cela est déjà fait par Python lors de la lecture. Il vous suffit donc de décider de la quantité de boucle que vous souhaitez effectuer lors du hachage sur ce tampon existant.
Mitar

5

J'ai programmé un module capable de hacher de gros fichiers avec différents algorithmes.

pip3 install py_essentials

Utilisez le module comme ceci:

from py_essentials import hashing as hs
hash = hs.fileChecksum("path/to/the/file.txt", "sha256")

3

Voici une solution Python 3, POSIX (pas Windows!) Qui utilise mmappour mapper l'objet en mémoire.

import hashlib
import mmap

def sha256sum(filename):
    h  = hashlib.sha256()
    with open(filename, 'rb') as f:
        with mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) as mm:
            h.update(mm)
    return h.hexdigest()

-2
import hashlib
user = input("Enter ")
h = hashlib.md5(user.encode())
h2 = h.hexdigest()
with open("encrypted.txt","w") as e:
    print(h2,file=e)


with open("encrypted.txt","r") as e:
    p = e.readline().strip()
    print(p)

2
Vous faites essentiellement ce echo $USER_INPUT | md5sum > encrypted.txt && cat encrypted.txtqui ne traite pas le hachage de fichiers, surtout pas les gros fichiers.
Murmel le

hashing! = encrypting
bugmenot123
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.