Hashing einer Datei in Python

96

Ich möchte, dass Python dem EOF vorliest, damit ich einen geeigneten Hash erhalten kann, egal ob es sich um sha1 oder md5 handelt. Bitte helfen Sie. Folgendes habe ich bisher:

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
user3358300
quelle
6
und was ist das problem
Isedev
1
Ich möchte, dass es eine Datei hashen kann. Ich brauche es, um bis zum EOF zu lesen, unabhängig von der Dateigröße.
user3358300
3
Genau das file.read()macht es - lesen Sie die gesamte Datei.
Isedev
In der Dokumentation zur read()Methode steht?
Ignacio Vazquez-Abrams
Sie sollten "Was ist Hashing?" Durchgehen.
Sharif Mamun

Antworten:

134

TL; DR verwenden Puffer, um nicht Tonnen von Speicher zu verwenden.

Ich glaube, wir kommen zum Kern Ihres Problems, wenn wir die Auswirkungen des Arbeitens mit sehr großen Dateien auf den Speicher betrachten . Wir wollen nicht, dass dieser böse Junge 2 Gigs RAM für eine 2-Gigabyte-Datei durchläuft , also müssen wir , wie Pasztorpisti betont , mit diesen größeren Dateien in Stücken umgehen!

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

Was wir getan haben, ist, dass wir unsere Hashes dieses bösen Jungen in 64-KB-Blöcken aktualisieren, während wir die praktische Dandy- Aktualisierungsmethode von hashlib befolgen . Auf diese Weise verbrauchen wir viel weniger Speicher als die 2 GB, die nötig wären, um den Kerl auf einmal zu hacken!

Sie können dies testen mit:

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

Hoffentlich hilft das!

All dies wird auch in der verknüpften Frage auf der rechten Seite beschrieben: Holen Sie sich MD5-Hash von großen Dateien in Python


Nachtrag!

Im Allgemeinen hilft es beim Schreiben von Python, sich daran zu gewöhnen, pep-8 zu folgen . Beispielsweise werden in Python Variablen normalerweise durch Unterstriche getrennt und nicht camelCased. Aber das ist nur Stil und niemand kümmert sich wirklich um diese Dinge, außer Menschen, die schlechten Stil lesen müssen ... vielleicht lesen Sie diesen Code in Jahren.

Randall Hunt
quelle
@ranman Hallo, ich konnte den Teil {0} ". format (sha1.hexdigest ()) nicht bekommen. Warum verwenden wir ihn, anstatt nur sha1.hexdigest () zu verwenden?
Belial
@Belial Was hat nicht funktioniert? Ich habe das hauptsächlich nur benutzt, um zwischen den beiden Hashes zu unterscheiden ...
Randall Hunt
@ranman Alles funktioniert, ich habe es einfach nie benutzt und es in der Literatur nicht gesehen. "{0}". Format () ... mir unbekannt. :)
Belial
1
Wie soll ich wählen BUF_SIZE?
Martin Thoma
1
Dies führt nicht zu den gleichen Ergebnissen wie die shasumBinärdateien. Die andere unten aufgeführte Antwort (die mit Memoryview) ist mit anderen Hashing-Tools kompatibel.
Tedivm
59

Für die korrekte und effiziente Berechnung des Hashwerts einer Datei (in Python 3):

  • Öffnen Sie die Datei im Binärmodus (dh 'b'zum Dateimodus hinzufügen ), um Probleme mit der Zeichenkodierung und der Konvertierung am Zeilenende zu vermeiden.
  • Lesen Sie nicht die gesamte Datei in den Speicher, da dies eine Verschwendung von Speicher ist. Lesen Sie es stattdessen nacheinander Block für Block und aktualisieren Sie den Hash für jeden Block.
  • Beseitigen Sie doppelte Pufferung, dh verwenden Sie keine gepufferten E / A-Vorgänge, da wir bereits eine optimale Blockgröße verwenden.
  • Verwenden Sie readinto()diese Option, um eine Pufferumwandlung zu vermeiden.

Beispiel:

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()
maxschlepzig
quelle
2
Woher wissen Sie, was eine optimale Blockgröße ist?
Mitar
1
@Mitar, eine Untergrenze ist das Maximum des physischen Blocks (traditionell 512 Byte oder 4 KB bei neueren Festplatten) und der Seitengröße des Systems (4 KB bei vielen Systemen, andere gängige Optionen: 8 KB und 64 KB). Dann führen Sie im Grunde genommen ein Benchmarking durch und / oder sehen sich veröffentlichte Benchmark-Ergebnisse und verwandte Arbeiten an (z. B. überprüfen Sie, welche aktuelle rsync / GNU cp / ... verwenden).
Maxschlepzig
Wäre resource.getpagesizehier von Nutzen, wenn wir versuchen wollten, es etwas dynamisch zu optimieren? Und was ist mit mmap?
jpmc26
@ jpmc26, getpagesize () ist hier nicht so nützlich - übliche Werte sind 4 KiB oder 8 KiB, etwas in diesem Bereich, dh etwas viel Kleineres als 128 KiB - 128 KiB ist im Allgemeinen eine gute Wahl. mmap hilft in unserem Anwendungsfall nicht viel, da wir die gesamte Datei nacheinander von vorne nach hinten lesen. mmap hat Vorteile, wenn das Zugriffsmuster eher wahlfrei ist, wenn mehrmals auf Seiten zugegriffen wird und / oder wenn die mmap die Verwaltung des Lesepuffers vereinfacht.
Maxschlepzig
3
Ich habe sowohl die Lösung von (1) @ Randall Hunt als auch (2) Ihre (in dieser Reihenfolge aufgrund des Datei-Cache wichtig) mit einer Datei von ca. 116 GB und einem sha1sum-Algorithmus verglichen. Lösung 1 wurde modifiziert, um einen Puffer von 20 * 4096 (PAGE_SIZE) zu verwenden und den Pufferparameter auf 0 zu setzen. Nur der Algorithmus von Lösung 2 wurde modifiziert (sha256 -> sha1). Ergebnis: (1) 3m37.137s (2) 3m30.003s. Die native sha1sum im Binärmodus: 3m31.395s
Bioinfornatics
18

Ich würde einfach vorschlagen:

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

Alle anderen Antworten hier scheinen zu kompliziert zu sein. Python puffert bereits beim Lesen (in idealer Weise, oder Sie konfigurieren diese Pufferung, wenn Sie mehr Informationen über den zugrunde liegenden Speicher haben). Daher ist es besser, Chunks einzulesen, die die Hash-Funktion als ideal erachtet, wodurch sie schneller oder zumindest weniger CPU-intensiv ist Berechnen Sie die Hash-Funktion. Anstatt die Pufferung zu deaktivieren und zu versuchen, sie selbst zu emulieren, verwenden Sie die Python-Pufferung und steuern, was Sie steuern sollten: Was der Verbraucher Ihrer Daten für die ideale Hash-Blockgröße hält.

Mitar
quelle
Perfekte Antwort, aber es wäre schön, wenn Sie Ihre Aussagen mit dem zugehörigen Dokument unterstützen würden: Python3 - open () und Python2 - open () . Trotz des Unterschieds zwischen beiden ist der Ansatz von Python3 ausgefeilter. Trotzdem habe ich die verbraucherorientierte Perspektive sehr geschätzt!
Murmel
hash.block_sizewird genauso dokumentiert wie die 'interne Blockgröße des Hash-Algorithmus'. Hashlib nicht finden es ideal . Nichts in der Paketdokumentation deutet darauf hin, dass Eingaben in der Größe update()bevorzugt hash.block_sizewerden. Es verbraucht nicht weniger CPU, wenn Sie es so nennen. Ihr file.read()Aufruf führt zu vielen unnötigen Objekterstellungen und überflüssigen Kopien aus dem Dateipuffer in Ihr neues Chunk-Byte-Objekt.
Maxschlepzig
Hashes aktualisieren ihren Status in Stücken block_size. Wenn Sie sie nicht in diesen Blöcken bereitstellen, müssen sie puffern und warten, bis genügend Daten angezeigt werden, oder bestimmte Daten intern in Blöcke aufteilen. Sie können das also nur von außen erledigen und dann vereinfachen, was intern passiert. Ich finde das ideal. Siehe zum Beispiel: stackoverflow.com/a/51335622/252025
Mitar
Das block_sizeist viel kleiner als jede nützliche Lesegröße. Alle nützlichen Block- und Lesegrößen sind Zweierpotenzen. Somit ist die Lesegröße für alle Lesevorgänge mit Ausnahme des möglicherweise letzten durch die Blockgröße teilbar. Beispielsweise beträgt die sha256-Blockgröße 64 Byte. Dies bedeutet, dass update()die Eingabe direkt verarbeitet werden kann, ohne dass ein Vielfaches von gepuffert werden muss block_size. Nur wenn der letzte Lesevorgang nicht durch die Blockgröße teilbar ist, muss er einmal bis zu 63 Byte puffern. Daher ist Ihr letzter Kommentar falsch und unterstützt nicht die Behauptungen, die Sie in Ihrer Antwort machen.
Maxschlepzig
Der Punkt ist, dass man die Pufferung nicht optimieren muss, da sie bereits beim Lesen von Python ausgeführt wird. Sie müssen sich also nur für die Anzahl der Schleifen entscheiden, die Sie beim Hashing über diesen vorhandenen Puffer ausführen möchten.
Mitar
5

Ich habe ein Modul programmiert, das große Dateien mit verschiedenen Algorithmen hashen kann.

pip3 install py_essentials

Verwenden Sie das Modul wie folgt:

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

Hier ist eine Python 3, POSIX-Lösung (nicht Windows!), Mit der mmapdas Objekt in den Speicher abgebildet wird.

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()
Antti Haapala
quelle
-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)
Ome Mishra
quelle
2
Sie tun im Grunde genommen, echo $USER_INPUT | md5sum > encrypted.txt && cat encrypted.txtwas sich nicht mit dem Hashing von Dateien befasst, insbesondere nicht mit großen.
Murmel
Hashing! = Verschlüsselung
Bugmenot123