Autor Beitrag
anno2007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 68

Win 7, Ubuntu
Delphi XE
BeitragVerfasst: 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:

ausblenden Delphi-Quelltext
1:
2:
3:
  filestr := FileToString('C:\Users\****\Desktop\testdatei.exe');
  str1 := Copy(filestr, 13145728);
  str2 := Copy(filestr, length(filestr)-3145728+1, length(filestr));


ausblenden 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 user profile iconNarses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am So 23.01.2011 um 12:41
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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



BeitragVerfasst: Sa 22.01.11 20:21 
Du willst Bytes auslesen und nimmst als Datentyp eine Zeichenkette? Das ist Unsinn. Nimm ein Byte Array.
anno2007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 68

Win 7, Ubuntu
Delphi XE
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: Sa 22.01.11 20:55 
user profile iconanno2007 hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 user profile iconFlamefires 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 68

Win 7, Ubuntu
Delphi XE
BeitragVerfasst: Di 25.01.11 02:27 
Danke für die Antworten :).
Ich konnte leider nicht früher antworten...

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 25.01.11 06:33 
user profile iconanno2007 hat folgendes geschrieben Zum zitierten Posting springen:
Kann ich dann einfach TFileStream.Create und dann mit .read auslesen?
Ja, wie jetzt auch.

user profile iconanno2007 hat folgendes geschrieben Zum zitierten Posting springen:
Wie setze ich dann den Zeiger ab wo ich auslesen will auf 3MB vor dem Dateiende?
Naja, ganz einfach...
ausblenden Delphi-Quelltext
1:
MyStream.Position := MyStream.Size - x;					
Wobei bei dir die Prüfung fehlt, ob die Datei überhaupt so groß ist.
anno2007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 68

Win 7, Ubuntu
Delphi XE
BeitragVerfasst: Di 25.01.11 17:59 
Hallo,
das habe ich jetzt so umgesetzt:
ausblenden volle Höhe 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>0then
    begin
      nsize := size;

      if (nsize > 3145728then
        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:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 68

Win 7, Ubuntu
Delphi XE
BeitragVerfasst: Di 25.01.11 18:43 
Ok, ich hab jetzt wieder die Fuktion:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: 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:

ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 68

Win 7, Ubuntu
Delphi XE
BeitragVerfasst: Di 25.01.11 19:19 
So gehts:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 68

Win 7, Ubuntu
Delphi XE
BeitragVerfasst: 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
BeitragVerfasst: 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