Autor |
Beitrag |
anno2007
      
Beiträge: 68
Win 7, Ubuntu
Delphi XE
|
Verfasst: Sa 22.01.11 20:03
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
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 22.01.11 20:20
Ich habe da mal was gemacht:
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.
Zuletzt bearbeitet von jaenicke am Sa 22.01.11 20:24, insgesamt 1-mal bearbeitet
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Sa 22.01.11 20:21
Du willst Bytes auslesen und nimmst als Datentyp eine Zeichenkette? Das ist Unsinn. Nimm ein Byte Array.
|
|
anno2007 
      
Beiträge: 68
Win 7, Ubuntu
Delphi XE
|
Verfasst: 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
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: 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
_________________ This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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 
      
Beiträge: 68
Win 7, Ubuntu
Delphi XE
|
Verfasst: 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
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 25.01.11 06:33
|
|
anno2007 
      
Beiträge: 68
Win 7, Ubuntu
Delphi XE
|
Verfasst: Di 25.01.11 17:59
Hallo,
das habe ich jetzt so umgesetzt:
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!
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 25.01.11 18:10
Also selbst für eine komplette 65 MiB Datei braucht es bei mir gerade einmal 1-2 Sekunden zur Erstellung des Hashes... (mit Indy, wobei du bei Delphi XE "HashStreamAsHex" statt "AsHex" + "HashValue" benutzen musst)
|
|
anno2007 
      
Beiträge: 68
Win 7, Ubuntu
Delphi XE
|
Verfasst: 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:
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:
91.203.145.37/~elementm/patch.php
|
|
elundril
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: 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
_________________ This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
|
|
anno2007 
      
Beiträge: 68
Win 7, Ubuntu
Delphi XE
|
Verfasst: 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:
forums.embarcadero.c....jspa?threadID=25766
/Jaaaaaa, jetzt geht es auch ums 2-fache schneller.
Vielen Dank an alle, besonder an jaenicke  .
|
|
Flamefire
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: 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 
      
Beiträge: 68
Win 7, Ubuntu
Delphi XE
|
Verfasst: Mi 26.01.11 11:48
Hey,
ok, das wird dann der nächste Schritt sein.
Werds mir anschauen,
Thx 
|
|
kwhk
Hält's aus hier
Beiträge: 4
Erhaltene Danke: 1
WinXP SP3, Win7, Win 8.1
Delphi7
|
Verfasst: 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
_________________ Hartmut
|
|