Delphi-PRAXiS
Seite 3 von 3     123   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   DEC 5.2 String hashen? (https://www.delphipraxis.net/192579-dec-5-2-string-hashen.html)

a.def 5. Mai 2017 08:47

AW: DEC 5.2 String hashen?
 
Eine Frage hätte ich noch. Ich habe gerade mal beobachtet wie die "E/A Bytes (Lesen)" im Taskmanager aussieht nachdem ich mein Testprogramm starten.
Mit meinem aktuellen Code und einem <Stream>.LoadFromFile() geht der o.g. Wert direkt in die Höhe. Ich würde mal sagen dort steht dann Programmgröße + 200KB.

Wenn ich folgenden Code verwende, dann passiert das nicht. Woran liegt das?
Delphi-Quellcode:
 aFileStream := TFileStream.Create(ParamStr(0), fmOpenRead or fmShareDenyWrite);
 try
  // Get hash from stream end
  SetLength(s, 32 * SizeOf(Byte));
  aFileStream.Position := aFileStream.Size - Length(s);
  aFileStream.Read(s[1], Length(s));

  ShowMessage(s);
 finally
  aFileStream.Free;
 end;
Wo ist also der Unterschied was den Wert "E/A Bytes (Lesen)" angeht zwischen den Zeilen unten?

Delphi-Quellcode:
// Verursaacht hohe (Programmgröße + ~100KB) "E/A Bytes (Lesen)" im Taskmanager
aByteStream := TBytesStream.Create;
aByteStream.LoadFromFile(ParamStr(0));

// Verursacht dieses Problem nicht
aFileStream := TFileStream.Create(ParamStr(0), fmOpenRead or fmShareDenyWrite);
Und: gibt es eine Lösung TBytesStream zu verwenden -ohne- dieses Problem im TaskManager? (TStringStream verursacht diese Anzeige übrigens auch)

Michael II 5. Mai 2017 09:26

AW: DEC 5.2 String hashen?
 
In deinem Codebeispiel oben liest du nur 32 Bytes:

aFileStream.Read(s[1], Length(s));


in deinem Beispiel unten das ganze File

aByteStream.LoadFromFile(ParamStr(0));

a.def 5. Mai 2017 09:46

AW: DEC 5.2 String hashen?
 
Mh ok klingt logisch und einleuchtend jetzt wo ich da mal drauf gucke.

Was ich gerade auch sehe.. folgende Zeile erzeugt denselben Effekt im Taskmanager:
Delphi-Quellcode:
// Stringlänge auf Streamlänge setzen
SetLength(sHash, aFileStream.Size);

Michael II 5. Mai 2017 09:57

AW: DEC 5.2 String hashen?
 
Genau der gleiche Grund, du verlangst u.U. viel Speicher.

a.def 5. Mai 2017 10:04

AW: DEC 5.2 String hashen?
 
Ich habe das jetzt mal so gelöst. Ich bin von TStringStream weg, da es hier als Masochismus betitelt wurde.
Leider aber wieder hin zu AnsiString. Mit String bekomme ich nur Salat als Ergebnis.
Es funktioniert, aber es ist bestimmt nicht perfekt.

Delphi-Quellcode:
       SetLength(s, aFileStream.Size - (32 * SizeOf(Byte)));
       aFileStream.Read(s[1], Length(s));
       s:= THashFunctions_Selftest.CalcHash(s);

       Result := s;
Das Problem mit dem hohen Speicher bekomme ich leider nicht weg, da ich das SetLength nicht einfach weglassen kann :(

nahpets 5. Mai 2017 10:11

AW: DEC 5.2 String hashen?
 
Naja, wenn Du 'nen Hash über 50 MB machen willst, musst Du auch die 50 MB einmal lesen.

Du wirst also immer einmal die Menge Speicher benötigen, die der Größer der EXE entspricht plus dem, was vom Programm ansonsten noch zum Ermitteln des Hashes benötigt wird.

Egal welche Methode Du wählst: Um mindestens einmal alles Lesen wirst Du nicht herumkommen.

Michael II 5. Mai 2017 10:59

AW: DEC 5.2 String hashen?
 
Wenn du den Hash selbst ermitteln musst, dann empfehle ich dir dies Stück für Stück zu tun.

Ich hatte mal Code gepostet, welcher gleich alles las. Das ist natürlich für extrem grosse Files dumm. [Aber für die meisten "normalen Fälle" ausreichend.]


Wenn du über das ganze File hashen willst, dann nimm doch eine vorgefertigte Methode, zum Beispiel

Delphi-Quellcode:
var b : TByte;
    rbs : rawbytestring;

...
...

b := System.hash.THashMD5.GetHashBytesFromFile(  );
oder
rbs := System.hash.THashMD5.GetHashStringFromFile(  );
Schau dir an, wie GetHashBytesFromFile programmiert ist.

...BytesFromFile liefert dir den 128Bit Hash in 16Bytes, und die ...StringFromFile liefert dir einen hex-String der Länge 32.

[Fritz hat mal eine Version gepostet, welche praktisch 1:1 gleich funktioniert. Die Version Fritz hasht aber bei der letzten Tranche über zu viele Bytes und liefert in fast nur in 1/1MB aller Fälle einen korrekten Wert.]



Wenn du nur über Teile eines Files hashen willst, dann könntest du es wie im Code unten tun (ich habe hier die Bytevariante ausgebaut, die hex Variante kannst du daraus ja leicht gewinnen):



Delphi-Quellcode:
procedure AddHashBytes( var lHasher: THashMD5; const AStream: TStream; von, bis : int64 );
const
   BufferSize = 64*1024;
var
  lStreamBytes: TBytes;
  Lies, SollLesen : int64;
  len : integer;
begin
  if bis >= AStream.size then bis := Astream.size-1;
  if von < 0 then von := 0;
  if bis >= von then
  begin
    setlength(lStreamBytes, BufferSize);
    AStream.Position := von;
    SollLesen := bis-von+1;
    while SollLesen > 0 do
    begin
      if SollLesen >= BufferSize then Lies := BufferSize else Lies := SollLesen;
      len := Astream.ReadData(lStreamBytes, Lies);
      if len = 0 then break;
      dec(SollLesen,len);
      lHasher.Update(lStreamBytes, len);
    end;
  end;
end;



function GetHashFromFile( aFileName : string; vonbis : array of int64 ) : TBytes;
var f : TFileStream;
   LMD5: THashMD5;
   i, len : integer;
   von, bis : int64;

begin
  f := TFileStream.Create( aFileName, fmOpenRead );
 try
  LMD5 := THashMD5.Create;
  LMD5.Reset;
  len := length(vonbis);
  if len < 2 then
  begin
      AddHashBytes( LMD5, f, 0, f.Size-1 );
  end
  else
  begin
      i := 0;
      while len-i >= 2 do
      begin
        von := vonbis[i];
        bis := vonbis[i+1];
        if von < 0 then von := f.Size+von;
        if bis < 0 then bis := f.Size+bis;

        AddHashBytes( LMD5, f, von, bis);
        inc(i,2);
      end;
  end;
  Result := LMD5.HashAsBytes;
 finally
  f.Free;
 end;
end;



Mögliche Aufrufe:
Beispiel 1:
Wenn du von Byte 0 bis 2000 und von Byte 2001 bis ans Ende hashen willst:

GetHashFromFile( filename, [0, 2000, 2001, maxint] );

Damit hashst du ja übers ganze File und erhältst natürlich exakt den gleichen Wert wie via
System.hash.THashMD5.GetHashBytesFromFile( filename );

Beispiel 2:
Wenn du Bytes 192435 - 192450 nicht "mithashen" willst:
GetHashFromFile( filename, [0, 192434, 192451, maxint] );

Beispiel 3:
Negative Werte:
Wenn du die letzten 16 Bytes des Files nicht hashen willst:
GetHashFromFile( filename, [0, -17] );




Ich hoffe, das hilft. Korrekturen bitte direkt an mich - Dankeschön.

p80286 5. Mai 2017 11:17

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von a.def (Beitrag 1370297)
Ich habe das jetzt mal so gelöst. Ich bin von TStringStream weg, da es hier als Masochismus betitelt wurde.
Leider aber wieder hin zu AnsiString. Mit String bekomme ich nur Salat als Ergebnis.
Es funktioniert, aber es ist bestimmt nicht perfekt.

Delphi-Quellcode:
       SetLength(s, aFileStream.Size - (32 * SizeOf(Byte)));
       aFileStream.Read(s[1], Length(s));
       s:= THashFunctions_Selftest.CalcHash(s);

       Result := s;
Das Problem mit dem hohen Speicher bekomme ich leider nicht weg, da ich das SetLength nicht einfach weglassen kann :(

Es ist mehr als einmal erwähnt worden, daß der HASH nicht über Chars/Strings/Text gebildet wird, sondern über die in der Datei enthaltenen Bytes.
du kannst natürlich eine String-Struktur als Aufbewahrungsort Deiner Bytes verwenden aber das schein mir hier so sinnvoll wie sich ein Loch in die Kniescheibe zu bohren und Marmelade hinein zu schmieren, machen kann man viel wenn der Tag lang ist.

Zitat:

Das Problem mit dem hohen Speicher bekomme ich leider nicht weg, da ich das SetLength nicht einfach weglassen kann
Wenn Du viele Daten auf einmal verarbeiten willst, dann benötigst Du auch viel Speicher. Das "SetLength" ist nur ein Symptom Deiner Datenverarbeitung.

Gruß
K-H

a.def 5. Mai 2017 12:02

AW: DEC 5.2 String hashen?
 
Zitat:

Wenn du über das ganze File hashen willst, dann nimm doch eine vorgefertigte Methode, zum Beispiel
Die Delphi-eigenen Methoden sind elendig langsam. Wenn THashMD5 bei mir 200ms braucht, braucht DEC für dieselbe Arbeit ~60ms.

Zitat:

Ich hoffe, das hilft. Korrekturen bitte direkt an mich - Dankeschön.
+
Zitat:

Es ist mehr als einmal erwähnt worden, daß der HASH nicht über Chars/Strings/Text gebildet wird, sondern über die in der Datei enthaltenen Bytes.
= *klick* :pale:

Delphi-Quellcode:
aByteStream := TBytesStream.Create;
try
 aByteStream.LoadFromFile(aFileName);

 aByteStream2 := TBytesStream.Create;
 try
  aByteStream2.Write(aByteStream.Bytes[0], aByteStream.Size); // aktuellen Stream zwischenspeichern
  sHash := THashFunctions_Selftest.CalcHash(Trim(TEncoding.ANSI.GetString(aByteStream2.Bytes))); // einen Hash der im neu erzeugten Stream enthaltenen Bytes erzeugen

  aByteStream.Size := aByteStream.Size + iHashLengthInBytes; // die Größe des ALTEN streams um maximale Länge des Hashs vergrößern (?)
  Move(AnsiString(sHash)[1], aByteStream.Bytes[aByteStream.Size - iHashLengthInBytes], iHashLengthInBytes); // Hash im alten Stream ablegen
  aByteStream.SaveToFile(aFileName); // Alten, modifizierten Stream speichern
 finally
  aByteStream2.Free;
 end;
finally
 aByteStream.FreeM
end;

// Eine kürzere Variante, ohne aByteStream2 wäre
sHash := THashFunctions_Selftest.CalcHash(Trim(TEncoding.ANSI.GetString(aByteStream.Bytes))); // einen Hash der im Stream enthaltenen Bytes erzeugen
aByteStream.Size := aByteStream.Size + iHashLengthInBytes; // die Größe des ALTEN streams um maximale Länge des Hashs vergrößern (?)
Move(AnsiString(sHash)[1], aByteStream.Bytes[aByteStream.Size - iHashLengthInBytes], iHashLengthInBytes); // Hash im alten Stream ablegen
aByteStream.SaveToFile(aFileName); // Stream speichern
Den Dateiinhalt mit am Ende hängenden Hash, lese ich so aus
Delphi-Quellcode:
if aByteStream.Size > iHashLengthInBytes then
 begin
  aByteStream.Read(aByteStream.Bytes[0], aByteStream.Size - iHashLengthInBytes);
  Result := THashFunctions_Selftest.CalcHash(Trim(TEncoding.ANSI.GetString(aByteStream.Bytes, 0, aByteStream.Size - iHashLengthInBytes)));
 end;

// Dateiinhalt könnte sein: _TEST_TEST_2B3CC5E89E09A889CC10D0021284AB06EA6FF72BCFE78110C8D9B71A6030375F
// Ausgelesen wird dann "_TEST_TEST_" und davon er Hash gebildet.

Michael II 5. Mai 2017 12:45

AW: DEC 5.2 String hashen?
 
Zitat:

Die Delphi-eigenen Methoden sind elendig langsam. Wenn THashMD5 bei mir 200ms braucht, braucht DEC für dieselbe Arbeit ~60ms.
Und was hat DEC mit MD5 zu tun?
Das liegt nicht an Delphi - du vergleichst hier zwei Hash Funktionen miteinander.


Ich habe dir anhand eines Beispiels [md5] zeigen wollen, wie du bei der Berechnung des MD5 Hashs einer beliebig grossen Datei immer nur einen Teil und nie das ganze File laden musst. [Du hattest wegen RAM schiesst in die Höhe geschrieben - und ich dachte ich mache dir eine Freude, wenn du siehst, dass dies nicht sein muss. ;-) und erst noch alles in Byte statt string]

Wenn du lieber SHA1 verwendest, dann verwendest du lieber SHA1 - dein Entscheid... sei mit deinem Code erst dann zufrieden, wenn eine Suche nach dem Wort "string" ins Leere läuft.

Wenn du alles in deinem Code Byte basiert machst, passieren auch nicht Fehler wie dieser hier:
Weg mit dem Trim [ich weiss, ich wiederhole mich] Grund: Solltest du mit diesem Code irgendwann den Hash Wert einer Datei mit Leerzeichen am Anfang oder Ende des Files berechnen wollen [OK, wie erwähnt bei .exe mind. am Anfang nicht der Fall], dann rechnest du diese nicht mit und die Berechnung geht in die Hose.


Delphi-Quellcode:
sHash := THashFunctions_Selftest.CalcHash([B]Trim([/B]TEncoding.ANSI.GetString(aByteStream.Bytes)));

a.def 5. Mai 2017 13:40

AW: DEC 5.2 String hashen?
 
Zitat:

Du hattest wegen RAM schiesst in die Höhe geschrieben - und ich dachte ich mache dir eine Freude, wenn du siehst, dass dies nicht sein muss.
Keine Sorge das hast du ;) Mein Code ist jetzt so aufgebaut, dass nur noch 1x maximal die Datei geladen wird (statt mehrfach). Vorher habe ich die glaube ich 3x geladen.

Zitat:

Weg mit dem Trim. ich weiss, ich wiederhole mich
Ohne Trim funktioniert das leider nicht. Ich weiß nicht genau warum aber ich schätze Trim entfernt mehr als gut ist.
Wenn ich die Bytes der Datei ohne Trim hashe und später auch ohne Trim wieder auslese und dann hashe, sind die Ergebnisse unterschiedlich. Warum weiß ich nicht.

Edit
-- es funktioniert aber wunderbar bisher ;)

p80286 5. Mai 2017 15:16

AW: DEC 5.2 String hashen?
 
Delphi-Quellcode:
Result := THashFunctions_Selftest.CalcHash([B]Trim(TEncoding.ANSI.GetString[/B](aByteStream.Bytes, 0, aByteStream.Size - iHashLengthInBytes)));
Warum kannst Du Dich nicht von dem verf***** String trennen?

Siehst Du in dem Beispiel vom Michael II irgendwo einen String oder ein Char?

U.U wäre es ganz vernünftig, wenn Du noch einmal ganz von vorne anfangen würdest.....

Gruß
K-H

a.def 5. Mai 2017 15:20

AW: DEC 5.2 String hashen?
 
Zitat:

Warum kannst Du Dich nicht von dem verf***** String trennen?
Was soll denn an dem String so schlimm sein? Er wird vollkommen korrekt in meine Exe geschrieben und auch geladen. Was will ich denn mehr?
Ich habe schon alles auf TBytes umgestellt. Aber den Rückgabewert möchte ich dennoch gerne als String haben.

P.S.: kein Grund ausfallend zu werden ;)

p80286 5. Mai 2017 15:41

AW: DEC 5.2 String hashen?
 
a) Ein String(char ist eine Interpretation eines Zahlenwertes. Je nach Codepage und oder anderer Annahme (nutzt Du vllt. EBCDIC?) produziert diese Interpretation beim selben Wert ein anderes Zeichen.
b) ich weiß jetzt nicht was an verflixt so ausfallend ist, aber vllt. ist es besser ich verabschiede mich ins WE.

Gruß
K-H

Michael II 5. Mai 2017 15:46

AW: DEC 5.2 String hashen?
 
Hallo a.def,

ich habe dir hier noch rasch die ähnliche Funktion für SHA1 geschrieben:

Delphi-Quellcode:
procedure AddSHA1HashBytes( var lHasher: THashSHA1; const AStream: TStream; von, bis : int64 );
const
   BufferSize = 64*1024;
var
  lStreamBytes: TBytes;
  Lies, SollLesen : int64;
  len : integer;
begin
  if bis >= AStream.size then bis := Astream.size-1;
  if von < 0 then von := 0;
  if bis >= von then
  begin
    setlength(lStreamBytes, BufferSize);
    AStream.Position := von;
    SollLesen := bis-von+1;
    while SollLesen > 0 do
    begin
      if SollLesen >= BufferSize then Lies := BufferSize else Lies := SollLesen;
      len := Astream.ReadData(lStreamBytes, Lies);
      if len = 0 then break;
      dec(SollLesen,len);
      lHasher.Update(lStreamBytes, len);
    end;
  end;
end;



function GetSHA1HashFromFile( aFileName : string; vonbis : array of int64 ) : TBytes;
var f : TFileStream;
   lSHA1: THashSHA1;
   i, len : integer;
   von, bis : int64;

begin
  f := TFileStream.Create( aFileName, fmOpenRead );
 try
  lSHA1 := THashSHA1.Create;
  lSHA1.Reset;
  len := length(vonbis);
  if len < 2 then
  begin
      AddSHA1HashBytes( lSHA1, f, 0, f.Size-1 );
  end
  else
  begin
      i := 0;
      while len-i >= 2 do
      begin
        von := vonbis[i];
        bis := vonbis[i+1];
        if von < 0 then von := f.Size+von;
        if bis < 0 then bis := f.Size+bis;

        AddSHA1HashBytes( lSHA1, f, von, bis);
        inc(i,2);
      end;
  end;
  Result := lSHA1.HashAsBytes;
 finally
  f.Free;
 end;
end;


Wenn du von Byte 0 bis 2000 und von Byte 2001 bis ans Ende hashen willst:

GetSHA1HashFromFile( filename, [0, 2000, 2001, maxint] );

Damit hashst du übers ganze File und erhältst natürlich exakt den gleichen Wert wie via
System.hash.THashSHA1.GetHashBytesFromFile( filename );

Beispiel 2:
Wenn du die 20 Bytes 192435 - 192454 nicht "mithashen" willst:
GetSHA1HashFromFile( filename, [0, 192434, 192455, maxint] );
Wenn du deinen SHA1 Hash Wert h ins Programm schreibst und h ab Position 192435 abgelegt ist, dann ist dies der Aufruf der Wahl ;-).

Beispiel 3:
Ein positiver Wert n wird interpretiert als Fileposition n.
Ein negativer Wert n wird interpretiert als Fileposition fsize+n.

GetSHA1HashFromFile( filename, [0, -21] );

Wenn du zum Beispiel an dein File einen SHA1 Hash (20 Bytes) angehängt hast, dann hashst du in diesem Beispiel über dein File, nicht aber über den angehängten Hash Wert.

Beispiel 4:
Du kannst natürlich über beliebig viele Intervalle hashen, zum Beispiel über drei:
GetSHA1HashFromFile( filename, [0,255, 256+pesig, 256+pesig+peofs-1, -20,maxint] );

Michael II 5. Mai 2017 16:16

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von a.def (Beitrag 1370322)
Keine Sorge das hast du ;) Mein Code ist jetzt so aufgebaut, dass nur noch 1x maximal die Datei geladen wird (statt mehrfach). Vorher habe ich die glaube ich 3x geladen.

Ich sorge mich aber schon ein wenig. Ich würde mal Pause machen, raus gehen, die 60€ für den Notar abarbeiten :roteyes: und dann wieder ran an den Code :-D.

Stell dir vor, dein File ist grösser als der zur Verfügung stehende Arbeitsspeicher. Dann muss dein OS Daten auf HD auslagern um von der HD lesen zu können :evil:.

Mach's mit "Einen Teil des Files lesen" - "Hash weiter rechnen" - "Den nächsten Teil des Files lesen" - "Hash weiter rechnen" - "Den nächsten Teil des Files lesen" - "Hash weiter rechnen" - "Den nächsten Teil des Files lesen" - "Hash weiter rechnen" - .

Dann kannst du deine Prozedur mit gutem Gewissen und entspannt in die freie Wildbahn entlassen.

a.def 5. Mai 2017 17:00

AW: DEC 5.2 String hashen?
 
Zitat:

Mach's mit "Einen Teil des Files lesen" - "Hash weiter rechnen" - "Den nächsten Teil des Files lesen" - "Hash weiter rechnen" - "Den nächsten Teil des Files lesen" - "Hash weiter rechnen" - "Den nächsten Teil des Files lesen" - "Hash weiter rechnen" - .
Was ich hierbei nicht verstehe... kommt das nicht auf dasselbe raus? Wird hier am Ende nicht auch die ganze Datei eingelesen?

Auszüge aus meinem Code aktuell (den ich auch verstehe). Hier wird der Hash am Ende der Datei gespeichert/geladen:
Delphi-Quellcode:
// Hash setzen
sHash := THashFunctions_Selftest.CalcHash(Trim(TEncoding.ANSI.GetString(aByteStream.Bytes)));
aByteStream.Size := aByteStream.Size + iHashLengthInBytes;
Move(AnsiString(sHash)[1], aByteStream.Bytes[aByteStream.Size - iHashLengthInBytes], iHashLengthInBytes);
aByteStream.SaveToFile(aFileName);
Delphi-Quellcode:
// Dateiinhalt lesen und den Hash bilden
if aByteStream.Size > iHashLengthInBytes then
 begin
  aByteStream.Read(aByteStream.Bytes[0], aByteStream.Size - iHashLengthInBytes);
  Result := THashFunctions_Selftest.CalcHash(Trim(TEncoding.ANSI.GetString(aByteStream.Bytes, 0, aByteStream.Size - iHashLengthInBytes)));
 end
else
 Result := '';
Delphi-Quellcode:
// Am Ende der Datei stehenden Hash auslesen
if aByteStream.Size > iHashLengthInBytes then
 begin
  aByteStream.Position := aByteStream.Size - iHashLengthInBytes;
  aByteStream.Read(aByteStream.Bytes[0], iHashLengthInBytes);
  Result := Trim(TEncoding.ANSI.GetString(aByteStream.Bytes, 0, iHashLengthInBytes));
 end
else
 Result := '';
Delphi-Quellcode:
aByteStream.LoadFromFile(aFileName);
wird nur ein einziges Mal ausgeführt, da ich die aktuelle Stream-Instanz immer an den Funktionsaufruf übergebe. Die drei Dinger da oben sthen alle in einer Funktion in der natürlich entschieden wird was man gerade abarbeiten will.

Vielleicht bin ich etwas naiv, dumm bin ich so oder so. Aber wenn beim Programmstart eines ~5MB Programms etwas auf die Festplatte ausgelagert wird, dann sollte sich der PC-Nutzer vielleicht mal Sorgen machen womit er seinen Arbeitsspeicher vollmüllt.

Ich würde gerne deinen Code übernehmen Michael. Aber ich verstehe ihn leider nicht und würde bald auch wieder vergessen was er macht. Mein Code ist zwar vielleicht nur 50% i.O. aber ich verstehe ihn.

Und bevor du dich wieder aufregst p80286, ja. Ich würde gerne vom String weg. Aber a) meinen Code beibehalten und b) vom String weg, das klappt bei mir halt nicht.
Die Lösung da oben funktioniert absolut 1A bisher.

Michael II 5. Mai 2017 19:08

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von a.def (Beitrag 1370359)
... Aber wenn beim Programmstart eines ~5MB Programms etwas auf die Festplatte ausgelagert wird, dann sollte sich der PC-Nutzer vielleicht mal Sorgen machen womit er seinen Arbeitsspeicher vollmüllt.

Ich meinte damit nur: Du solltest beim Programmieren immer auch an den Speicher denken... wenn du das ganze File reinlädst, dann könnte das viel Speicher kosten.

Michael II 5. Mai 2017 19:38

AW: DEC 5.2 String hashen?
 
Hallo a.def

das hilft eventuell.

MD5 und SHA1 Hash Algorithmen sind absichtlich so konstruiert, dass du nicht von Anfang an das ganze Argument (= alle Filedaten) benötigst.
D.h. du kannst das File nach und nach lesen und jederzeit mit Update den Hash Wert der bereits gelesenen Filedaten berechnen. Du hast also nach jedem Schritt zwei Argumente um fortzufahren mit deiner Berechnung, den momentanen Hash Wert und die noch zu lesenden Filedaten.
Wenn du jeweils nur 128KB Filedaten liest, dann benötigst du an Daten nur diese 128KB im Arbeitsspeicher und ein paar Bytes für den Hash Wert.



Hier ein Beispiel mit MD5 (mit SHA1 läuft's genau "gleich"):

Mit

procedure SchreibeMD5HashAnsEndeDesFiles( const filename : string );

schreibst du die HashBytes ans Ende des Files:

Delphi-Quellcode:
procedure HashAnsEndeDesFilesSchreiben( filename : string; hash : TBytes );
var f : TFileStream;
begin
  f := TFileStream.Create( filename, fmOpenWrite );
 try
  f.Position := f.Size;
  f.Write( hash[0], length( hash ) );
 finally
  f.Free;
 end;
end;


procedure SchreibeMD5HashAnsEndeDesFiles( const filename : string );
var hash, hash2 : TBytes;
begin
  hash := System.hash.THashMD5.GetHashBytesFromFile( filename ); // Die in Delphi System.Hash definierte Funktion
  // hash2 := GetMD5HashFromFile( filename , [] ); // Du könntest auch "unsere" verwenden
  HashAnsEndeDesFilesSchreiben( filename, hash );
end;

Mit

function PruefeMD5HashAmEndeDesFiles( const filename : string ) : boolean;

kannst du überprüfen, ob die HashBytes am Ende des Files OK sind.

Delphi-Quellcode:
function LiesHashAmEndeDesFiles( const filename : string; len : integer ) : TBytes;
var f : TFileStream;
begin
  f := TFileStream.Create( filename, fmOpenRead );
 try
  f.Position := f.Size - len ;
  SetLength( Result, len );
  f.Read( Result, len );
 finally
  f.Free;
 end;
end;


function PruefeMD5HashAmEndeDesFiles( const filename : string ) : boolean;
var hashAmFileEnde, hash : TBytes;
begin
  hash := GetMD5HashFromFile( filename, [0,-17] ); // Wir lesen bis size-17, Den Hash Wert size-16..size-1 verarbeiten wir nicht
  hashAmFileEnde := LiesHashAmEndeDesFiles( filename, 16 ); // Wir lesen den Hash Wert am Ende des Files
  Result := CompareMem( hash, hashAmFileEnde, 16 ); // Wir vergleichen den berechneten Wert mit jenem am Fileende
end;


Testroutine:
1. Zuerst werden die Hashbytes ans Ende des Files geschrieben.

2. Hier wird gezeigt, wie du den Hash Wert am Ende des Files überprüfen kannst:

Delphi-Quellcode:
procedure pruefen;
var filename : string;
    allesok : boolean;
begin
  filename := 'C:\Users\Michael\Desktop\1GB.txt';

  // Ans Fileende schreiben:
  SchreibeMD5HashAnsEndeDesFiles( filename ); // 1.
  // Prüfen, ob der HashWert OK ist:
  allesok := PruefeMD5HashAmEndeDesFiles( filename ); // 2.

  ShowMessage( 'Check File : ' + allesok.ToInteger.ToString );
end;

a.def 5. Mai 2017 20:56

AW: DEC 5.2 String hashen?
 
Ich bin dir wirklich sehr dankbar für alles. Aber leider funktioniert dein Code bei mir nicht.
Für folgendes erhalte ich auch nur Salat
Delphi-Quellcode:
 // _TEST_TEST_D36E844639D6B4B9ADAF9A11C2ABF841CCE43E94C57539C03CC9524545DD8E8D
 ShowMessage( TEncoding.ANSI.GetString(
    GetSHA1HashFromFile(aFileName, [0, -64])
   )
   );
Deine Lösung ist meiner deutlich überlegen das ist klar. Aber ich denke ich bleibe einfach bei meiner. Ich verstehe sie und kann dran rumbasteln wenn was nicht funktioniert.

nahpets 5. Mai 2017 21:24

AW: DEC 5.2 String hashen?
 
Ein Hash ist kein lesbarere Text, sondern schlicht eine mehr oder weniger lange Bytefolge (landläufig als Salat bezeichnet).

a.def 5. Mai 2017 21:36

AW: DEC 5.2 String hashen?
 
Ich lass das jetzt einfach so. Es funktioniert was will ich denn mehr?
Ist mir jetzt ehrlich gesagt auch zu sehr ein HeckMeck den String da noch in irgendwas anderes umzuwandeln und was weiß ich noch alles.

Michael II 5. Mai 2017 23:12

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von a.def (Beitrag 1370373)
Ich bin dir wirklich sehr dankbar für alles. Aber leider funktioniert dein Code bei mir nicht.
Für folgendes erhalte ich auch nur Salat
Delphi-Quellcode:
 // _TEST_TEST_D36E844639D6B4B9ADAF9A11C2ABF841CCE43E94C57539C03CC9524545DD8E8D
 ShowMessage( TEncoding.ANSI.GetString(
    GetSHA1HashFromFile(aFileName, [0, -64])
   )
   );

Du machst einen Fehler SHA1 liefert 160Bit/20Byte Werte. Du musst also
hash := GetSHA1HashFromFile( filename, [0,-21] );
berechnen, wenn du den Hash Wert des Files ohne den angehängten (20 Byte) Hash Wert berechnen willst.

So geht's:

SHA1 ans Ende des Files schreiben durch Aufruf von
procedure SchreibeSHA1HashAnsEndeDesFiles( const filename : string );

Delphi-Quellcode:
procedure HashAnsEndeDesFilesSchreiben( filename : string; hash : TBytes );
var f : TFileStream;
begin
  f := TFileStream.Create( filename, fmOpenWrite );
 try
  f.Position := f.Size;
  f.Write( hash[0], length( hash ) );
 finally
  f.Free;
 end;
end;


procedure SchreibeSHA1HashAnsEndeDesFiles( const filename : string );
var hash, hash2 : TBytes;
begin
  hash := System.hash.THashSHA1.GetHashBytesFromFile( filename ); // Delphi system.hash
  // hash2 := GetSHA1HashFromFile( filename , [] ); // oder wir verwenden "unsere" SHA1 Funktion
  HashAnsEndeDesFilesSchreiben( filename, hash );
end;

Den Hashwert am Ende des Files überprüfen:
function PruefeSHA1HashAmEndeDesFiles( const filename : string ) : boolean;


Delphi-Quellcode:
function LiesHashAmEndeDesFiles( const filename : string; len : integer ) : TBytes;
var f : TFileStream;
begin
  f := TFileStream.Create( filename, fmOpenRead );
 try
  f.Position := f.Size - len ;
  SetLength( Result, len );
  f.Read( Result, len );
 finally
  f.Free;
 end;
end;

function PruefeSHA1HashAmEndeDesFiles( const filename : string ) : boolean;
var hashAmFileEnde, hash : TBytes;
begin
  hash := GetSHA1HashFromFile( filename, [0,-21] ); // SHA1 erzeugt 160bit (20Byte) Werte
  hashAmFileEnde := LiesHashAmEndeDesFiles( filename, 20 );
  Result := CompareMem( hash, hashAmFileEnde, 20 );
end;


Testen:

Delphi-Quellcode:
procedure pruefen_SHA1;
var filename : string;
    allesok : boolean;
begin
  filename := 'C:\Users\Michael\Desktop\1GB.txt';

  // Ans Fileende schreiben:
  SchreibeSHA1HashAnsEndeDesFiles( filename );
  // Prüfen, ob der HashWert OK ist:
  allesok := PruefeSHA1HashAmEndeDesFiles( filename );

  ShowMessage( 'Check File : ' + allesok.ToInteger.ToString );
end;

a.def 5. Mai 2017 23:45

AW: DEC 5.2 String hashen?
 
Ganz dumme Frage:

wenn ich den String-Hash direkt speichere und er sagen wir mal 64 Byte lang ist, wieviele Byte nimmt denn sein Äquivalent der "Salat" ein. Ebenefalls exakt 64 Byte?
Weil im Editor sieht das irgendwie kürzer aus.

Michael II 5. Mai 2017 23:47

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von a.def (Beitrag 1370376)
Ich lass das jetzt einfach so. Es funktioniert was will ich denn mehr?
Ist mir jetzt ehrlich gesagt auch zu sehr ein HeckMeck den String da noch in irgendwas anderes umzuwandeln und was weiß ich noch alles.

Es ist gerade umgekehrt. SHA1 liefert 20 Bytes und diese 20 Bytes werden dann typischerweise (wenn du den Wert in irgend einer Arbeit o.ä. druckst) als hex Wert dargestellt.

Wenn du dir also deine Bytes lieber hex anschaust, dann wandle sie doch einfach um:

Delphi-Quellcode:
function TBytesToHex( b : TBytes ) : RawByteString;
var i : integer;
begin
  Result := '';
  for i := 0 to length(b)-1 do Result := Result + inttohex(b[i]);
  Result := lowercase(Result);
end;

a.def 5. Mai 2017 23:50

AW: DEC 5.2 String hashen?
 
Ob SHA1 2 MD5 ist doch egal. Ging mir nur darum, ob da auch 64 Bytes landen, wenn der Hash in der lesbaren Version auch 64 Bytes lang ist. Verstehe halt noch nicht wie "lang" der Salat wirklich ist weil der manchmal kürzer, manchmal länger aussieht als die lesbare Version :P

Michael II 6. Mai 2017 00:11

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von a.def (Beitrag 1370380)
Ganz dumme Frage:

wenn ich den String-Hash direkt speichere und er sagen wir mal 64 Byte lang ist, wieviele Byte nimmt denn sein Äquivalent der "Salat" ein. Ebenefalls exakt 64 Byte?
Weil im Editor sieht das irgendwie kürzer aus.


Ich glaube du bezeichnest den "Byte Array" als Salat:

Salat:
(212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126)

hex
'd41d8cd98f00b204e9800998ecf8427e'

In einem Byte kannst du die binären Werte 00000000..11111111 speichern, dezimal geschrieben die Werte von 0..255 [dein Salat] und hex 00 bis ff [deine Strings].

212 ist hex d4
29 ist hex 1d
140 ist hex 8c
usw.

16 Bytes Salat ergeben also einen 32 stelligen String.

https://de.wikipedia.org/wiki/Hexadezimalsystem

a.def 6. Mai 2017 00:22

AW: DEC 5.2 String hashen?
 
Mh Ok ich verstehe. Das heißt Bytes Array ist immer von der Länge her die Hälfte vom Hex.
Ich gucke morgen nochmal rein und gucke, ob ich was ändern kann.

Kann ich dafür TByteStream behalten oder ist TFileStream ein Muss?

himitsu 6. Mai 2017 10:27

AW: DEC 5.2 String hashen?
 
Und ein Salt kann natürlich beliebig groß sein, außer bei Hash-/Verschlüsselungsfunktionen, die einen Salt mit fester Länge erfordern.
Ein Salt ist einfach nur ein "beliebiger" Startwert, mit dem man quasi den Hash initialisiert, damit bei selben Eingangsstrings nicht der selbe Ausgangshash raus kommt, also z.B. das selbe Passwort in verschiedenen Systemen nicht den selben Hash hat und man es so auch mit Rückrechnen/Rainbowtables nicht so leicht hat, um ein passendes Equivalent zu finden.

Wordpress z.B. nutzt einen von der Database separat gespeicherten globalen Salt, für alle Passwörter.
Alternativ kann man auch zu jedem Hash einen eigenen Salt generieren und beim/im Hash hinterlegen.

Zitat:

Zitat von Michael II (Beitrag 1370381)
Wenn du dir also deine Bytes lieber hex anschaust, dann wandle sie doch einfach um:
Delphi-Quellcode:
function TBytesToHex( b : TBytes ) : RawByteString;
var i : integer;
begin
  Result := '';
  for i := 0 to length(b)-1 do Result := Result + inttohex(b[i]);
  Result := lowercase(Result);
end;

BinToHex?

Michael II 7. Mai 2017 11:06

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von a.def (Beitrag 1370385)
Mh Ok ich verstehe. Das heißt Bytes Array ist immer von der Länge her die Hälfte vom Hex.
Ich gucke morgen nochmal rein und gucke, ob ich was ändern kann.

Kann ich dafür TByteStream behalten oder ist TFileStream ein Muss?

Wenn du den in einem Byte gespeicherten Wert im hex System interpretieren und als Zeichenkette speichern willst, dann benötigst du pro Byte 2 Zeichen (aus {0..9,A..B} ).

Wenn du den in einem Byte gespeicherten Wert im binären System interpretieren und als Zeichenkette speichern willst, dann benötigst du pro Byte 8 Zeichen ( aus {0..1} ).


TBytesStream oder TFileStream?
(siehe auch weiter oben)
Du willst mit Daten, welche in einem File enthalten sind arbeiten.
TBytesStream kann Daten aufnehmen, welche als Bytes interpretiert werden. Via LoadFromFile kannst du die Daten aufnehmen, welche in einem File enthalten sind.
TFileStream kann keine Daten aufnehmen, sondern gibt dir nur die Möglichkeit (via read/write/..) auf die Daten eines Files zuzugreifen. (Der Datenspeicher ist also hier quasi deine Festplatte o.ä..)

Da du für die Berechnung von SHA1, MD5 Hashs nicht alle Daten (Filedaten) aufs Mal benötigst, musst du auch nicht alle aufs Mal einlesen.

Nimm wie in den Meldungen weiter oben erwähnt TFileStream. Dann hast du deine Daten auf der Platte und liest jeweils nur soviel Daten ein, wie du gerade benötigst.


Und noch kurz zu der Verwendung von trim(..) in deinem Code.
Wenn du für eine beliebige Datei einen Hash Wert berechnen willst, dann darfst du trim nicht verwenden.

Delphi-Quellcode:
daten : TBytes;
a1 : ansistring;
...
...
a1 := trim( TEncoding.ANSI.GetString( daten ) );
Falls zum Beispiel daten[0] den Wert 32 enthält, dann entspricht dies nach TEncoding.ANSI.GetString dem Leerzeichen und mit trim(..) schneidest du dieses Leerzeichen ab. Wenn du nun über a1 "hashst", dann ermittelst du somit nicht den Hash von daten. [Wenn du TFileStream verwenden wirst und deine exe stückweise einliest (und jeweils die Hash Update Methode aufrufst), dann denk daran: Nicht trimmen ;-).]

Und wenn du's nicht selbst schreiben willst: Du kannst ja 1:1 den weiter oben geposteten Code verwenden fürs Schreiben und Checken des SHA1/MD5 Hashs.

Ich glaube, du hast nun alles, was du benötigst. Ich habe längst fertig :-).

Daniel 7. Mai 2017 11:37

AW: DEC 5.2 String hashen?
 
Zitat:

Zitat von Michael II (Beitrag 1370470)
Ich glaube, du hast nun alles, was du benötigst. Ich habe längst fertig :-).

"a.def" ist wohl ebenfalls fertig und hat auf eigenen Wunsch das Forum verlassen.
Ich mache daher mal zu.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:10 Uhr.
Seite 3 von 3     123   

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz