Seite 1 von 1
Hashwert einer Datei berechnen und Fortschritt zurückgeben
Verfasst: Dienstag 29. Juni 2010, 16:43
von anogayales
Hi Community,
Ich möchte den Hashwert einer Datei berechnen, und während dieser Berechnung den Fortschritt der Hashwert Berechnung angeben.
Das ganze bringt mehrere Probleme mit sich:
Bevor ich mit der Berechnung beginnnen kann, muss ich erstmal wissen wie viel lines die Datei hat. Dies macht die Berechnung sehr langsam.
Hier mein Code, er funktioniert, ist aber in keinster Weise effizient:
Code: Alles auswählen
checksum = hashlib.sha224()
with open(filepath, "rb") as f:
lines = f.readlines() # Dauert Lange :P
line_length = len(lines)
counter = 0
print "Starte"
for line in f: # for line in lines macht hier das selbe oder?
checksum.update(line)
# Informiere Nutzer über Fortschritt
counter += 1
print "Ende"
f.close()
Hat jemand einen Vorschlag für eine andere Art und Weise wie man an dieses Problem ran geht? Vielleicht das ganze mit der Dateigröße abschätzen?
Grüße,
anogayales
Re: Hashwert einer Datei berechnen und Fortschritt zurückgeb
Verfasst: Dienstag 29. Juni 2010, 17:18
von Warhead
Hi anogayales,
Du könntest z.B. mittels os.path.getsize(<path>) die Größe in Byte ermitteln und danach via "<file>.read(<byte>)" die Datei Häppchenweise auslesen. Wenn Du die Häppchen so groß wählst, dass sie 1/100tel der Gesamtdateigröße entsprechen, dann hast Du damit auch eine erstmal essentielle, generische Basis für die Fortschrittsbestimmung/Anzeige.
Optimierung kann danach immer noch betrieben werden

.
Re: Hashwert einer Datei berechnen und Fortschritt zurückgeb
Verfasst: Dienstag 29. Juni 2010, 19:28
von str1442
Unter Debian und ähnlichen GNU/Linux Systemen könntest du den effizienten pv Befehl benutzen, hier mit md5sum:
Statt md5sum könntest du hier auch ein kleines Skript benutzen, welches dir den Hashwert im entsprechendem Format ausgibt (bei dir sha224) oder einen anderen, eingebauten Befehl benutzen. Um Warheads Idee mittels iter()s speziellem Verhalten zu implementieren:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import hashlib
from functools import partial
def main():
def accum(checksum, chunk):
checksum.update(chunk)
return checksum
print reduce(accum, iter(partial(sys.stdin.read, 1024), ""), hashlib.sha224()).hexdigest()
return 0
if __name__ == "__main__":
sys.exit(main())
Wahlweise natürlich ohne den etwas ineffizienten reduce() Aufruf, partial() oder diese spezielle Möglichkeit, iter() zu nutzen.
Code: Alles auswählen
str1442:~$ cat > example_text << EOF
> Dies ist ein Beispieltext.
> EOF
str1442:~$ python sha224.py < example_text
0611fed92052d649d00b6f0c862ae9f5d953636ebc15ed334b185f88
str1442:~$ pv example_text | python sha224.py
27B 0:00:00 [ 326kB/s] [===============================================================================================================>] 100%
0611fed92052d649d00b6f0c862ae9f5d953636ebc15ed334b185f88
Für die Zeilenanzahl dann das wc Kommando, aber die brauchst du ja sowieso nur für die Fortschrittberechnung der Operation. Solltest du eine Lösung wünschen, die nur in Python geschrieben ist (vllt. falls du Windows benutzt), gibt es für Fortschrittsbalken auch irgendwo ein Modul in
http://pypi.python.org/ . Dem müsstest du dann für jeden "chunk" die angegebene Grösse und vorher die Gesamtgrösse der Datei (os.stat()) übermitteln.
Re: Hashwert einer Datei berechnen und Fortschritt zurückgeb
Verfasst: Dienstag 29. Juni 2010, 20:35
von anogayales
Vielen Dank für eure bisherigen Antworten.
Das ganze soll aber in einer GUI laufen (Qt). Demnach darf eigentlich nichts auf der Konsole angezeigt werden. Zusätzlich muss das ganze auch auf Windows laufen, dennoch vielen Dank!
Grüße,
anogayales
Re: Hashwert einer Datei berechnen und Fortschritt zurückgeb
Verfasst: Dienstag 29. Juni 2010, 21:59
von BlackJack
@anogayales: Unabhängig vom GUI-Toolkit kann man das einfach mit einer Callback-Funktion machen. Einfach wie schon vorgeschlagen die Dateigrösse erfragen und dann in Blöcken verarbeiten und nach jedem Block die aktuell verarbeitete Bytezahl per Callback melden. Was die Callback-Funktion dann damit macht -- per ``print`` ausgeben oder in einer GUI darstellen -- muss die Datei-Hash-Funktion ja nicht wissen.
Re: Hashwert einer Datei berechnen und Fortschritt zurückgeb
Verfasst: Mittwoch 30. Juni 2010, 14:21
von anogayales
Also ich hab das ganze mal sehr naiv implementiert, bei mir werden aber noch ein paar Bytes bei der Berechnung ausgelassen. Liegt wohl an der Integerdivision:
Code: Alles auswählen
checksum = hashlib.sha224()
filesize = os.path.getsize(filepath)
stepsize = filesize/100
print "The file size is %s bytes" % filesize
print "The stepsize is %s bytes" % stepsize
print "The error is %s" % (filesize - stepsize * 100)
with open(filepath, "rb") as f:
counter = 0
for line in f.read(stepsize):
while(True):
checksum.update(line)
counter += stepsize
if counter >= filesize:
break
f.seek(counter)
# Calculate remainder and also use it to calculate the hash value
remainder = filesize - f.tell()
print "The remainder is %s" % remainder
checksum.update(f.read(remainder))
print "Current position %s" % f.tell()
print "The file size is %s bytes" % filesize
break
f.close()
Hat da jemand noch diverse Tricks im Ärmel?
Wenn ich die Bedingung if counter >= filesize: rausnehme läuft mir das Ding ins unendliche. Ich hab immer gedacht eine Datei sei endlich

Komisch, dass er mir da keine Fehlermeldung ausspuckt?!?
Leider bekomme ich damit nicht den gleichen Hashwert wie bei der obigen Referenzimplementierung raus. Sollte halt schon bezüglich des Ergebnisses invariant sein.
Grüße,
anogayales
Re: Hashwert einer Datei berechnen und Fortschritt zurückgeb
Verfasst: Mittwoch 30. Juni 2010, 15:19
von BlackJack
@anogayales: Ich würde davon abraten die Blockgrösse zu berechnen. Wenn die Datei <100 Bytes ist kommt da 0 heraus, das ist sicher lustig.

Wenn sie >100 aber immer noch klein ist, werden in jedem Schleifendurchlauf nur wenige Bytes gelesen, das ist unsinnig.
Das ist auch extrem verwirrend ausgedrückt mit dem `seek()`. Lies einfach in einer Schleife solange Blöcke mit einer festen Grösse bis keine Daten mehr kommen. Dann sparst Du Dir auch irgendwelche Spässe mit dem berechnen von der Blockgrösse und dem letzten Block. Da stimmt wahrscheinlich auch irgendetwas nicht, denn wenn Du die ``while``-Scheife verlässt, dann ist `counter` ja schon grösser oder gleich der Dateigrösse, also ist zu dem Zeitpunkt schon alles verarbeitet.
Die Schleife würde ohne diesen Test ewig weiterlaufen, weil ein `read()` am Ende einer Datei eine leere Zeichenkette liefert. Immer und immer wieder. *Das* ist aber auch ein prima Abbruchkriterium.
Du darfst den `counter` auch nicht einfach um `stepsize` erhöhen, denn ein `read(stepsize)` kann auch weniger als `stepsize` Bytes liefern -- nämlich am Ende der Datei wenn gar nicht mehr genug Bytes da sind, oder eben ganz am Ende wenn immer 0 Bytes gelesen werden.
Code: Alles auswählen
import os
from functools import partial
from hashlib import sha224
def hash_file(file_obj,
hasher,
callback=lambda byte_count: None,
blocksize=4096):
byte_count = 0
for block in iter(partial(file_obj.read, blocksize), ''):
hasher.update(block)
byte_count += len(block)
callback(byte_count)
return hasher.hexdigest()
def main():
filename = 'test.iso'
filesize = os.path.getsize(filename)
def print_progress(byte_count):
print '\r%d/%d %6.2f' % (byte_count,
filesize,
100.0 * byte_count / filesize),
with open(filename) as iso_file:
print hash_file(iso_file, sha224(), print_progress)
Re: Hashwert einer Datei berechnen und Fortschritt zurückgeb
Verfasst: Mittwoch 30. Juni 2010, 15:37
von anogayales
Vielen Dank für deine ausführliche Darlegung!
EDIT:
HABS! Man muss die Datei, in meinem Fall mit open(filename, "rb"). Sonst berechnet es was anderes, nur was?
Frage:
Gibt es eigentlich eine Möglichkeit mit partial den zusätlichen parameter VOR die anderen Parameter zu schieben und nicht dahinter?
Grüße,
anogayales