Entwickler-Ecke
Dateizugriff - Datei Byte weise auslesen
anno2007 - Sa 22.01.11 20:03
Titel: Datei Byte weise auslesen
Hey,
ich muss für einen MD5 Dateivergleich eine Datei auslesen.
Allerdings nehmen wir nur die ersten 3145728 und die letzens 3145728 Bytes der Datei zum auslesen.
In PHP ist das recht flott.
In Delphi allerdings leese ich bis jetzt die ganze Datei in einen String und teile den dann auf:
Delphi-Quelltext
1: 2: 3:
| filestr := FileToString('C:\Users\****\Desktop\testdatei.exe'); str1 := Copy(filestr, 1, 3145728); str2 := Copy(filestr, length(filestr)-3145728+1, length(filestr)); |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| function FileToString(const AFileName: string): AnsiString; var f: TFileStream; l: Integer; begin Result := ''; f := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); try l := f.Size; if L > 0 then begin SetLength(Result, l); F.ReadBuffer(Result[1], l); end; finally F.Free; end; end; |
Hat da jemand eine schnellere Lösung?
Weil das ist wirklich extrem langsam...
Thx
anno2007
Moderiert von
Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am So 23.01.2011 um 12:41
jaenicke - Sa 22.01.11 20:20
Ich habe da mal was gemacht:
http://www.delphi-forum.de/viewtopic.php?t=99933
Weiter unten ist auch eine Stream-Implementierung verlinkt.
Damit komme ich bei zeichenweisem Auslesen auf meinem aktuell schnellsten PC auf knapp 100 MiB/s.
// EDIT:
Und von der SSD gehts noch schneller. Letztlich ist hier die sequentielle Festplattengeschwindigkeit der limitierende Faktor, nicht mehr die bei vielen kleineren Zugriffen.
Delete - Sa 22.01.11 20:21
Du willst Bytes auslesen und nimmst als Datentyp eine Zeichenkette? Das ist Unsinn. Nimm ein Byte Array.
anno2007 - Sa 22.01.11 20:32
Ja, ok ich bruache schon Strings um die der MD5 Funktion zu übergeben.
@jaenicke, das sieht gut aus, Danke :)
jaenicke - Sa 22.01.11 20:51
Nebenbei: Da du eh nur zwei Blöcke auslesen willst, könntest du das auch direkt mit TFileStream machen. Denn du brauchst ja auch damit nicht die ganze Datei auslesen. Schließlich kannst du die Position setzen und dann die gewünschte Anzahl Byte auslesen.
Deine beiden Strings kannst du vorher ja auch auf die entsprechende Länge setzen.
Denn was bei dir so lange dauert ist ja danach das Umkopieren des unnötig großen Strings.
elundril - Sa 22.01.11 20:55
anno2007 hat folgendes geschrieben : |
Ja, ok ich bruache schon Strings um die der MD5 Funktion zu übergeben. |
Aha, warum? MD5 sollte doch eigentlich Byteweise funktionieren und der String wird in der MD5-Funktion sicher umgewandelt, oder? Kannst du demnach die Funktion einfach umbauen?
lg elundril
jaenicke - Sa 22.01.11 21:16
Mit der Implementierung in den Indy Komponenten geht das z.B. auch extrem schnell, indem man die Datei mit
Flamefires Stream-Implementierung lädt und den Stream direkt an die MD5 Funktion füttert.
Damit sollte von der ganzen Datei sehr schnell der Hash erstellt werden.
anno2007 - Di 25.01.11 02:27
Danke für die Antworten :).
Ich konnte leider nicht früher antworten...
jaenicke hat folgendes geschrieben : |
Nebenbei: Da du eh nur zwei Blöcke auslesen willst, könntest du das auch direkt mit TFileStream machen. Denn du brauchst ja auch damit nicht die ganze Datei auslesen. Schließlich kannst du die Position setzen und dann die gewünschte Anzahl Byte auslesen.
Deine beiden Strings kannst du vorher ja auch auf die entsprechende Länge setzen.
Denn was bei dir so lange dauert ist ja danach das Umkopieren des unnötig großen Strings. |
Ja, das hab ich mir auch gedacht.
Kann ich dann einfach TFileStream.Create und dann mit .read auslesen?
Wie setze ich dann den Zeiger ab wo ich auslesen will auf 3MB vor dem Dateiende?
Würde mich über eine weiter Antwort sehr freuen :).
Vielen Dank
anno07
jaenicke - Di 25.01.11 06:33
anno2007 hat folgendes geschrieben : |
Kann ich dann einfach TFileStream.Create und dann mit .read auslesen? |
Ja, wie jetzt auch.
anno2007 hat folgendes geschrieben : |
Wie setze ich dann den Zeiger ab wo ich auslesen will auf 3MB vor dem Dateiende? |
Naja, ganz einfach...
Delphi-Quelltext
1:
| MyStream.Position := MyStream.Size - x; |
Wobei bei dir die Prüfung fehlt, ob die Datei überhaupt so groß ist.
anno2007 - Di 25.01.11 17:59
Hallo,
das habe ich jetzt so umgesetzt:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| function FileToMd5(const AFileName: string): AnsiString; var res1, res2 : Ansistring; f : TFileStream; size, nsize : integer; begin res1 := ''; res2 := '';
f := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); try size := f.Size; if (size>0) then begin nsize := size;
if (nsize > 3145728) then nsize := 3145728;
SetLength(res1, nsize); SetLength(res2, nsize);
F.ReadBuffer(res1[1], nsize);
F.Position := size-nsize; F.ReadBuffer(res2[1], nsize); end; finally F.Free; end;
result := md5string(res1 + res2 + inttostr(size)); end; |
Funktioniert einwandfrei, nur ist das ganze leider nicht schneller gewordern. Ich vermute es liegt an der langsamen MD5 Funktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| function MD5String(const Input:string):string; var MD5: IMD5; begin MD5 := GetMD5; MD5.Init; MD5.Update(TByteDynArray(RawByteString(Input)), Length(RawByteString(Input))); result := LowerCase(MD5.AsString); end; |
Aber ein versuch war es Wert! :)
Danke!
anno2007 - Di 25.01.11 18:43
Ok, ich hab jetzt wieder die Fuktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| function MD5String(const Input:string):string; var IDMD5:TIdHashMessageDigest5; begin IDMD5:=TIdHashMessageDigest5.Create; try Result:=LowerCase(IDMD5.HashStringAsHex(Input)); finally IDMD5.Free; end; end; |
Es sieht so aus als wäre die ein gutes Stück schneller, nur leider wieder dieses Problem:
http://www.delphi-forum.de/viewtopic.php?t=103363
Kannst du mir sagen wie man die in AnsiString umschreibt?
Ich habe leider keine Ahnung wo ich das suchen muss.
Ganze Dateien zu hashen dauert zu lange. Ich bin halt dabei ein Update-System zu schreiben und dann nehme ich halt von jeder Datei die ersten und die letzen 3 MB und ihre Dateigröße. Das geht auch in PHP passabel schnell:
http://91.203.145.37/~elementm/patch.php
elundril - Di 25.01.11 18:58
ich bin mal ganz verrückt und sag einfach mal: ich hab keine ahnung aber versuch doch mal das hier:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| function MD5String(const Input:string):string; var IDMD5:TIdHashMessageDigest5; begin IDMD5:=TIdHashMessageDigest5.Create; try Result:=LowerCase(IDMD5.HashStringAsHex(AnsiString(Input))); finally IDMD5.Free; end; end; |
lg elundril
anno2007 - Di 25.01.11 19:19
So gehts:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| function MD5String(const Input:string):string; var IDMD5:TIdHashMessageDigest5; begin IDMD5:=TIdHashMessageDigest5.Create; try Result:=LowerCase(IDMD5.HashStringAsHex(Input, TEncoding.Default)); finally IDMD5.Free; end; end; |
In dem Thread hab ich das gefunden:
https://forums.embarcadero.com/thread.jspa?threadID=25766
/Jaaaaaa, jetzt geht es auch ums 2-fache schneller.
Vielen Dank an alle, besonder an jaenicke ;).
Flamefire - Mi 26.01.11 10:43
Trotzdem wandelst du einen Stream in einen String um, um den dann zu hashen. Es gibt doch, wie schon erwähnt, HashStreamAsHex statt HashStringAsHex.
Dann hast du keinen solchen fehleranfälligen Code. Solltest also deine Berechnung wirklich auf Bytes statt auf "Zeichen" umstellen. Besonders da deine Datei ja keine Zeichen (oder wenn dann rein zufällig) enthällt.
Mit TFastFilestream (von mir) kannst du sehr schnell Dateiene einlesen. Das ganze in einen MemoryStream und dann so in die MD5 funktion. Ist das sauberste.
anno2007 - Mi 26.01.11 11:48
Hey,
ok, das wird dann der nächste Schritt sein.
Werds mir anschauen,
Thx :)
kwhk - Sa 29.01.11 19:36
Ich habe nur ein paar Anmerkungen...
1.) Die Variablen für Dateilänge usw. müssen int64 statt integer (4 byte) sein, sonst gibt es Probleme mit Dateien, die länger als 2,2 GB sind. TFileStream.Size liefert die korrekte Länge als int64.
2.) Wenn die ersten und die letzten 3 MB der Datei gelesen werden sollen, muss festgelegt werden, was zu tun ist wenn...
2.1 die Datei kürzer als 3 MB ist : s1: komplette Datei s2: Leerstring
2.2 die Datei kürzer als 6 MB ist : s1: 3 MB s2: Rest der Datei
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!