472 Stimmen

Erzeugen einer MD5-Prüfsumme über eine Datei

Gibt es eine einfache Möglichkeit, MD5-Prüfsummen einer Liste von Dateien in Python zu erzeugen (und zu überprüfen)? (Ich habe ein kleines Programm, an dem ich arbeite, und ich möchte die Prüfsummen der Dateien bestätigen).

662voto

quantumSoup Punkte 25833

Sie können verwenden hashlib.md5()

Beachten Sie, dass Sie manchmal nicht die gesamte Datei im Speicher unterbringen können. In diesem Fall müssen Sie Teile von 4096 Bytes nacheinander einlesen und an den md5 Methode:

import hashlib
def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

Nota: hash_md5.hexdigest() gibt die hexadezimale Zeichenkette Darstellung für den Digest, wenn Sie nur die gepackten Bytes benötigen, verwenden Sie return hash_md5.digest() so dass Sie nicht zurück konvertieren müssen.

336voto

Omnifarious Punkte 52299

Es gibt einen Weg, der eine schöne Erinnerung ist ineffizient .

einzelne Datei:

import hashlib
def file_as_bytes(file):
    with file:
        return file.read()

print hashlib.md5(file_as_bytes(open(full_path, 'rb'))).hexdigest()

Liste der Dateien:

[(fname, hashlib.md5(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]

Es sei jedoch daran erinnert, dass MD5 ist bekanntermaßen defekt und sollte nicht für irgendeinen Zweck verwendet werden, da die Analyse von Schwachstellen sehr schwierig sein kann und es unmöglich ist, jede mögliche zukünftige Verwendung Ihres Codes auf Sicherheitsprobleme hin zu analysieren. IMHO sollte es einfach aus der Bibliothek entfernt werden, damit jeder, der es benutzt, gezwungen ist, es zu aktualisieren. Also, hier ist, was Sie stattdessen tun sollten:

[(fname, hashlib.sha256(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]

Wenn Sie nur einen 128-Bit-Digest benötigen, können Sie Folgendes tun .digest()[:16] .

Sie erhalten eine Liste von Tupeln, wobei jedes Tupel den Namen der Datei und den Hash-Wert enthält.

Auch hier stelle ich Ihre Verwendung von MD5 stark in Frage. Sie sollten mindestens SHA1 verwenden, und angesichts kürzlich entdeckte Schwachstellen in SHA1 wahrscheinlich nicht einmal das. Manche Leute meinen, solange man MD5 nicht für "kryptografische" Zwecke verwendet, sei alles in Ordnung. Aber die Dinge haben die Tendenz, umfassender zu sein, als man zunächst annimmt, und Ihre beiläufige Schwachstellenanalyse kann sich als völlig fehlerhaft erweisen. Am besten ist es, wenn Sie sich angewöhnen, von Anfang an den richtigen Algorithmus zu verwenden. Man tippt einfach nur ein paar andere Buchstaben ein, das ist alles. Das ist gar nicht so schwer.

Dieser Weg ist etwas komplexer, aber speichereffizient :

import hashlib

def hash_bytestr_iter(bytesiter, hasher, ashexstr=False):
    for block in bytesiter:
        hasher.update(block)
    return hasher.hexdigest() if ashexstr else hasher.digest()

def file_as_blockiter(afile, blocksize=65536):
    with afile:
        block = afile.read(blocksize)
        while len(block) > 0:
            yield block
            block = afile.read(blocksize)

[(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.md5()))
    for fname in fnamelst]

Und noch einmal: MD5 ist kaputt und sollte eigentlich nicht mehr verwendet werden:

[(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.sha256()))
    for fname in fnamelst]

Auch hier gilt: Sie können [:16] nach dem Aufruf von hash_bytestr_iter(...) wenn Sie nur einen 128-Bit-Digest haben wollen.

36voto

rsandwick3 Punkte 566

Ich füge natürlich nichts grundlegend Neues hinzu, sondern füge diese Antwort hinzu, bevor ich den Status eines Kommentators erreicht habe, und die Code-Regionen machen die Dinge klarer - jedenfalls speziell, um die Frage von @Nemo aus der Antwort von Omnifarious zu beantworten:

Ich habe zufällig ein wenig über Prüfsummen nachgedacht (ich kam hierher, um nach Vorschlägen für Blockgrößen zu suchen) und habe herausgefunden, dass diese Methode schneller sein kann, als man erwarten würde. Wenn man die schnellste (aber ziemlich typische) timeit.timeit o /usr/bin/time Ergebnis jeder der verschiedenen Methoden der Prüfsummenbildung einer Datei von ca. 11 MB:

$ ./sum_methods.py
crc32_mmap(filename) 0.0241742134094
crc32_read(filename) 0.0219960212708
subprocess.check_output(['cksum', filename]) 0.0553209781647
md5sum_mmap(filename) 0.0286180973053
md5sum_read(filename) 0.0311000347137
subprocess.check_output(['md5sum', filename]) 0.0332629680634
$ time md5sum /tmp/test.data.300k
d3fe3d5d4c2460b5daacc30c6efbc77f  /tmp/test.data.300k

real    0m0.043s
user    0m0.032s
sys     0m0.010s
$ stat -c '%s' /tmp/test.data.300k
11890400

Es sieht also so aus, dass sowohl Python als auch /usr/bin/md5sum etwa 30 ms für eine 11 MB große Datei benötigen. Die relevante md5sum Funktion ( md5sum_read in der obigen Auflistung) ist dem von Omnifarious ziemlich ähnlich:

import hashlib
def md5sum(filename, blocksize=65536):
    hash = hashlib.md5()
    with open(filename, "rb") as f:
        for block in iter(lambda: f.read(blocksize), b""):
            hash.update(block)
    return hash.hexdigest()

Zugegeben, es handelt sich um einzelne Läufe (die mmap sind immer ein bisschen schneller, wenn mindestens ein paar Dutzend Durchläufe gemacht wurden), und meiner hat normalerweise einen zusätzlichen f.read(blocksize) nachdem der Puffer erschöpft ist, aber es ist einigermaßen wiederholbar und zeigt, dass md5sum auf der Kommandozeile ist nicht unbedingt schneller als eine Python-Implementierung...

EDIT: Entschuldigung für die lange Verzögerung, ich habe schon lange nicht mehr darauf geschaut, aber um @EdRandalls Frage zu beantworten, werde ich eine Adler32-Implementierung aufschreiben. Allerdings habe ich die Benchmarks dafür noch nicht durchgeführt. Es ist im Grunde das Gleiche wie CRC32 gewesen wäre: Anstelle der init-, update- und digest-Aufrufe ist alles ein zlib.adler32() anrufen:

import zlib
def adler32sum(filename, blocksize=65536):
    checksum = zlib.adler32("")
    with open(filename, "rb") as f:
        for block in iter(lambda: f.read(blocksize), b""):
            checksum = zlib.adler32(block, checksum)
    return checksum & 0xffffffff

Beachten Sie, dass dies mit der leeren Zeichenkette beginnen muss, da sich Adlersummen in der Tat unterscheiden, wenn sie mit Null beginnen, im Gegensatz zu ihrer Summe für "" das ist 1 -- CRC kann beginnen mit 0 stattdessen. Die AND -Zeichen wird benötigt, um ihn zu einem 32-Bit-Ganzzahlwert ohne Vorzeichen zu machen, der sicherstellt, dass er in allen Python-Versionen den gleichen Wert zurückgibt.

36voto

Boris V Punkte 10136

In Python 3.8 +, können Sie die Funktion Zuweisungsoperator := (zusammen mit hashlib ) wie folgt:

import hashlib
with open("your_filename.txt", "rb") as f:
    file_hash = hashlib.md5()
    while chunk := f.read(8192):
        file_hash.update(chunk)

print(file_hash.digest())
print(file_hash.hexdigest())  # to get a printable str instead of bytes

Erwägen Sie die Verwendung von hashlib.blake2b anstelle von md5 (einfach ersetzen md5 con blake2b im obigen Ausschnitt). Es ist kryptographisch sicher und schneller als MD5.

24voto

johnson Punkte 2776
hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X