Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Neuen Beitrag zur Code-Library hinzufügen (https://www.delphipraxis.net/33-neuen-beitrag-zur-code-library-hinzufuegen/)
-   -   Delphi Dateien byteweise vergleichen (https://www.delphipraxis.net/151352-dateien-byteweise-vergleichen.html)

HeikoAdams 14. Mai 2010 11:08


Dateien byteweise vergleichen
 
Hallo,
da ich hier noch nichts in der Richtung gefunden habe, stelle ich meinen Code zum byteweisen Vergleich von Dateien hier einfach mal zur Diskussion:
Delphi-Quellcode:
{
Version 1.2.1:
- added try-finally-block specially requested by himitsu.

Version 1.2:
- rewritten to squeez code to minimum. Thanks to Neutral General

Version 1.1:
- rewritten for shorter code. Thanks to himitsu
- using TMemoryStream instead of TFileStream. Thanks to DeddyH
}
function ByteCompFiles(const aSourceFile, aDestFile: string): Boolean;
var
  SourceFile, DestFile: TMemoryStream;
begin
  SourceFile := TMemoryStream.Create;
  try
    DestFile := TMemoryStream.Create;
    try
      SourceFile.LoadFromFile(aSourceFile);
      DestFile.LoadFromFile(aDestFile);
      Result := (SourceFile.Size = DestFile.Size) and
                CompareMem(SourceFile.Memory, DestFile.Memory, SourceFile.Size);      
    finally
      DestFile.Free;
    end;
  finally
    SourceFile.Free;
  end;
end;
Vorschläge zur Verbesserung oder Optimierung des Codes sind gerne willkommen :)

himitsu 14. Mai 2010 11:20

Re: Dateien byteweise vergleichen
 
Eine byteweise Verarbeitung ist extrem langsam ... ließ ganze Blöcke aus und vergleiche diese z.B. mit CompareMemory deren Inhalt.

Jedesmal Seek aufzurufen ist auch unnötig, da Read den Positionszeiger schon verschiebt.

Read ohne Auswerten des Rückgabewertes zu verarbeiten ist nicht gut, da Lesefehler irgnoriert werden könnten. (verwende besser ReadBuffer oder werte das Result aus)

Ansonsten sollten schon einige Codes dafür in der DP existieren
(Einer ist z.B. in meinem Hier im Forum suchenSearchSameFiles verbaut)

Und eigentlich dürfte bestimmt auch Einer in der CodeLib zu finden sein. :gruebel:
[edit] jupp, ist gleich der 11. Eintrag in "Dateien / Laufwerke"
http://www.delphipraxis.net/internal...ct.php?t=60645

Die Fehlerbehandlung ist sehr schlecht, denn wann werden die Streams freigegeben, wenn eine Exception auftritt oder wenn die Dateigrößen unterschiedlich sind? (garnicht)

DeddyH 14. Mai 2010 11:27

Re: Dateien byteweise vergleichen
 
Ich hab mir mal erlaubt, eine bessere Fehlerbehandlung einzubringen und die ganzen "break" und "exit" zu eliminieren. Ob das ständige Seek notwendig ist, lass ich mal dahingestellt. BTW: Wäre es nicht schneller, TMemoryStreams zu verwenden und das mit CompareMemory abzuhandeln?
Delphi-Quellcode:
function ByteCompFiles(const aSourceFile, aDestFile: string): Boolean;
var
  SourceFile : TFileStream;
  DestFile: TFileStream;
  nCounter: Int64;
  ReadByteSource: Byte;
  ReadByteDest: Byte;
begin
  try
    Result := True;
    nCounter := 0;
    //Schreibzugriffe unterbinden, ansonsten wäre die ganze Aktion witzlos
    SourceFile := TFileStream.Create(aSourceFile, fmOpenRead or fmShareDenyWrite);
    try
      DestFile := TFileStream.Create(aDestFile, fmOpenRead or fmShareDenyWrite);
      try
        //Dateigröße ist unterschiedlich -> Abbruch
        Result := (SourceFile.Size = DestFile.Size);

        while Result and (nCounter < SourceFile.Size) do
        begin
          SourceFile.Seek(nCounter, soFromBeginning);
          SourceFile.Read(ReadByteSource, 1);

          DestFile.Seek(nCounter, soFromBeginning);
          DestFile.Read(ReadByteDest, 1);

          //Unterschied gefunden -> Abbruch
          Result := (ReadByteSource = ReadByteDest);

          Inc(nCounter);
          Sleep(10);
        end;
      finally
        DestFile.Free;
      end;
    finally
      SourceFile.Free;
    end;
  except
    Result := False
  end;
end;

himitsu 14. Mai 2010 11:30

Re: Dateien byteweise vergleichen
 
Delphi-Quellcode:
try
  ...
except
  Result := False
end;
Dieses sollte besser ganz raus, denn bei einem Zugriffsfehler (z.B. fehlende Rechte) würde einfach nur FALSE ausgegeben, aber der Grund bleibt "geheim".
Außerdem könnte es ja dennoch sein, daß beide Dateien identisch sind, welches dann ntürlich ein falsches Ergebnis liefern würde.

die beiden kürzesten Quellcodes (nicht kürzeste Laufzeit) wären also:
Delphi-Quellcode:
function ByteCompFiles(const aSourceFile, aDestFile: string): Boolean;
var
  SourceFile, DestFile: TFileStream;
  Counter: Int64;
  ByteSource, ByteDest: Byte;
begin
  SourceFile := TFileStream.Create(aSourceFile, fmOpenRead or fmShareDenyWrite);
  try
    DestFile := TFileStream.Create(aDestFile, fmOpenRead or fmShareDenyWrite);
    try
      Result := SourceFile.Size = DestFile.Size;
      Counter := 0;
      while Result and (nCounter < SourceFile.Size) do
      begin
        SourceFile.ReadBuffer(ByteSource, 1);
        DestFile.ReadBuffer(ByteDest, 1);
        Result := ReadByteSource = ReadByteDest;
        Inc(Counter);
      end;
    finally
      DestFile.Free;
    end;
  finally
    SourceFile.Free;
  end;
end;

function ByteCompFiles(const aSourceFile, aDestFile: string): Boolean;
var
  SourceFile, DestFile: TFileStream;
  Counter: Int64;
  ByteSource, ByteDest: Byte;
begin
  SourceFile := TFileStream.Create(aSourceFile, fmOpenRead or fmShareDenyWrite);
  DestFile := nil;
  try
    DestFile := TFileStream.Create(aDestFile, fmOpenRead or fmShareDenyWrite);
    Result := SourceFile.Size = DestFile.Size;
    Counter := 0;
    while Result and (Counter < SourceFile.Size) do
    begin
      SourceFile.ReadBuffer(ByteSource, 1);
      DestFile.ReadBuffer(ByteDest, 1);
      Result := ReadByteSource = ReadByteDest;
      Inc(Counter);
    end;
  finally
    DestFile.Free;
    SourceFile.Free;
  end;
end;

DeddyH 14. Mai 2010 11:31

Re: Dateien byteweise vergleichen
 
Das überlass ich aber dem TE ;)

[edit] Da würde ich die erste Variante aber vorziehen, da man dafür nicht unbedingt gewisse Kenntnisse über das Free braucht, um sie zu verstehen. [/edit]

Luckie 14. Mai 2010 12:06

Re: Dateien byteweise vergleichen
 
Wenn die Funktion sowieso nur wahr oder falsch zurück gibt, kann man dann nicht einfach einen Hash nehmen?

mkinzler 14. Mai 2010 12:08

Re: Dateien byteweise vergleichen
 
Wenn eine Wahscheinlichkeit reicht

himitsu 14. Mai 2010 12:19

Re: Dateien byteweise vergleichen
 
Zitat:

Zitat von mkinzler
Wenn eine Wahscheinlichkeit reicht

Ein Hash wäre (vorallem wenn beide Dateien auf dem selben Datenträger liegen) wohl schneller, aber wie mkinzler sagen wollte "ein Hash ist nicht eindeutig".

Bei einem CRC32 gibt es schon bei Dateigrößen bis 5 Byte je Hash mindestens 256 Dateien mit diesem Hash.

HeikoAdams 14. Mai 2010 13:00

Re: Dateien byteweise vergleichen
 
Ich habe meinen ersten Code auf die erste Version des Codes von himitsu aus seinem letzten Vorschlag aktualisiert und nebenbei noch nach Anregung von DeddyH auf TMemoryStreams umgebaut. Weitere Vorschläge sind erlaubt :wink:

himitsu 14. Mai 2010 13:12

Re: Dateien byteweise vergleichen
 
Wie gesagt, das langsame byteweise Lesen.
- Lesen über einen Puffer
- und der Einfachheit halber die Zählrichtung vom Counter umgedreht

Bedenke auch, daß bei TMemoryStream alles im RAM landet, welches bei größeren Dateien Probleme bereiten kann.
[add] Aber wenn eh schon alles im RAM liegt, dann kann man natürlich alles auf einmal vergleichen ... siehe nachfolgend Neutral General.

Delphi-Quellcode:
function ByteCompFiles(const aSourceFile, aDestFile: string): Boolean;
var
  SourceFile, DestFile: TFileStream;
  Counter: Int64;
  BufSize: Integer;
  SourceBuffer, DestBuffer: array[0..32767] of Byte;
begin
  SourceFile := TFileStream.Create(aSourceFile, fmOpenRead or fmShareDenyWrite);
  try
    DestFile := TFileStream.Create(aDestFile, fmOpenRead or fmShareDenyWrite);
    try
      Result := SourceFile.Size = DestFile.Size;
      Counter := SourceFile.Size;
      while Result and (Counter > 0) do
      begin
        if Counter >= SizeOf(SourceBuffer) then
          BufSize := SizeOf(SourceBuffer)
        else
          BufSize := Counter;
        SourceFile.ReadBuffer(SourceBuffer, BufSize);
        DestFile.ReadBuffer(DestBuffer, BufSize);
        Result := CompareMem(@SourceBuffer, @DestBuffer, BufSize);
        Dec(Counter, BufSize);
      end;
    finally
      DestFile.Free;
    end;
  finally
    SourceFile.Free;
  end;
end;

Neutral General 14. Mai 2010 13:12

Re: Dateien byteweise vergleichen
 
Verbesserungsvorschlag:

Delphi-Quellcode:
function ByteCompFiles(const aSourceFile, aDestFile: String): Boolean;
var SourceFile, DestFile: TMemoryStream;
begin
  SourceFile := TMemoryStream.Create;
  DestFile := TMemoryStream.Create;
  try
    SourceFile.LoadFromFile(aSourceFile);
    DestFile.LoadFromFile(aDestFile);
    Result := (SourceFile.Size = DestFile.Size) and
              (CompareMem(SourceFile.Memory, DestFile.Memory, SourceFile.Size));      
  finally
    DestFile.Free;
    SourceFile.Free;
  end;
end;

HeikoAdams 14. Mai 2010 13:27

Re: Dateien byteweise vergleichen
 
Zitat:

Zitat von Neutral General
Verbesserungsvorschlag

committed :-D siehe Post #1

himitsu 14. Mai 2010 13:57

Re: Dateien byteweise vergleichen
 
Zitat:

Zitat von HeikoAdams
committed :-D siehe Post #1

Schau dir aber dazu bitte meine beiden Codes aus Beitrag #4 an ... speziell die Streams und die zugehörigen Try-Finally.

Ein Resourcen-Schutzblock (Try-Finally) fängt immer direkt nach der Resourcen-Beschaffung an,
um diese Resource auch richtig zu schützen/freizugeben.

Delphi-Quellcode:
{resource reservieren}
try
  ...
finally
  {resource freigeben}
end;
und bei 2 Resourcen wäre es dann
Delphi-Quellcode:
{resource 1 reservieren}
try
  ...
  {resource 2 reservieren}
  try
    ...
  finally
    {resource 2 freigeben}
  end;
  ...
finally
  {resource 1 freigeben}
end;
Also muß es so gemacht werden, um einen korrekten Schutz zu gewährleisten:
Delphi-Quellcode:
SourceFile := TStream.Create(...);
try
  DestFile := TStream.Create(...);
  try
    SourceFile.LoadFromFile(...);
    DestFile.LoadFromFile(...);
    ...
  finally
    DestFile.Free;
  end;
finally
  SourceFile.Free;
end;
Folgendes geht nur unter zwei Annahmen:
- man kann der Resource einen prüfbaren Wert für "Resource nicht reserviert" zuweisen
- man kann ausschließen, daß beim Freigeben keine Exception auftritt
Delphi-Quellcode:
DestFile := nil;
SourceFile := TFileStream.Create(aSourceFile, fmOpenRead or fmShareDenyWrite);
try
  DestFile := TFileStream.Create(aDestFile, fmOpenRead or fmShareDenyWrite);
  SourceFile.LoadFromFile(...);
  DestFile.LoadFromFile(...);
  ...
finally
  {if Assigned(DestFile) then} DestFile.Free; // aber dieses ist schon in .Free eingebaut
  SourceFile.Free;
end;
Das DestFile:=nil; wurde in vorherigen Beispielen nur aus logischer Sicht nach SourceFile:=TFileStream.Create verschoben und weil dieses keine Exteption auslösen kann (es sei denn der Stack ist vorher schon geschrottet, aber dann ist eh alles egal).

OK, wenn man zusätzlich noch ausschließen kann, daß keine Exception beim Erstellen auftritt, dann ginge auch dieses, aber sowas ist beim Erstellen von Klassen eben nicht sichergestellt.
Delphi-Quellcode:
{resource 1 reservieren}
{resource 2 reservieren}
try
  ...
finally
  {resource 2 freigeben}
  {resource 1 freigeben}
end;

Neutral General 14. Mai 2010 14:04

Re: Dateien byteweise vergleichen
 
Hi,

Du machst also wenn du 3 Klassen erstellst 3 try-finally Blöcke? Finde ich etwas übertrieben. Beim erstellen eines Objekts SOLLTE eigentlich nichts schief gehen...

himitsu 14. Mai 2010 14:08

Re: Dateien byteweise vergleichen
 
Zitat:

Zitat von Neutral General
Du machst also wenn du 3 Klassen erstellst 3 try-finally Blöcke?

Ja, genau das.

Zitat:

Zitat von Neutral General
Beim erstellen eines Objekts SOLLTE eigentlich nichts schief gehen...

OutOfMemory (kein Speicher für die Klasse) und andere Exceptions im Konstructor können also nie auftreten?

Die Ausnahmen für die Zusammenfassung der Try-Finally hatte ich grade oben noch mit dazueditiert.

Neutral General 14. Mai 2010 14:09

Re: Dateien byteweise vergleichen
 
Jaaa.. Natürlich KANN etwas passieren. Aber 3 try-finally Blöcke. Das sieht ja schrecklich aus und da blickt man doch nicht mehr durch :shock:

Naja meinetwegen. Richtiger ist deine Version .. schon. Stimmt schon. Aber wenn ich jetzt keine Software für die NASA schreibe oder so werde ich glaube ich ganz fahrlässig darauf verzichten :mrgreen:

himitsu 14. Mai 2010 14:17

Re: Dateien byteweise vergleichen
 
Zitat:

Zitat von Neutral General
Aber wenn ich jetzt keine Software für die NASA schreibe oder so werde ich glaube ich ganz fahrlässig darauf verzichten :mrgreen:

In deinen eigenen Programme kannst'e das "gern" machen.
Aber bei öffentlichen Codes für Andere ... was wäre denn, wenn die ESA :zwinker: die neuen Raketensteuerprogramme in Delphi programmiert und dafür deinen Code verwendet?

Und wie gesagt, mit einer kleinen Umstelltung kannst'e hier die beiden Streams auch mit nur einem Try-Finally absichern. :angel2:

Neutral General 14. Mai 2010 14:20

Re: Dateien byteweise vergleichen
 
Wenn sie von mir Code haben will, dann bekommt sie um jede Zeile ein try-finally wenn sie will xD

Wenn sie Code von mir findet, dann ist das ja nicht mein Problem wenn die den 1:1 übernehmen und 1 try-finally fehlt ^^

HeikoAdams 14. Mai 2010 14:22

Re: Dateien byteweise vergleichen
 
Zitat:

Zitat von Neutral General
Wenn sie Code von mir findet, dann ist das ja nicht mein Problem wenn die den 1:1 übernehmen und 1 try-finally fehlt ^^

Ich bin zwar kein Jurist, aber damit dürftest Du wohl Recht haben. Wer ungeprüft Code von dritten übernimmt, dem ist nicht (mehr) zu helfen :wall:

himitsu 14. Mai 2010 14:26

Re: Dateien byteweise vergleichen
 
Wenn man aber jetzt davon ausgehen darf, daß alle Codes potentiell fehlerhaft/unsicher sind,
dann kann man auch gleich alles selber neu schreiben und niemals Fremdcodes verwenden.
Und gerade bei unserer Codelib geh ist erstmal davon aus, daß darin geprüfter/sicherer Code enthalten ist, bzw. daß derartiger Code keine bekannten Fehler enthält. :zwinker:

HeikoAdams 14. Mai 2010 14:45

Re: Dateien byteweise vergleichen
 
Zitat:

Zitat von himitsu
Wenn man aber jetzt davon ausgehen darf, daß alle Codes potentiell fehlerhaft/unsicher sind,
dann kann man auch gleich alles selber neu schreiben und niemals Fremdcodes verwenden.
Und gerade bei unserer Codelib geh ist erstmal davon aus, daß darin geprüfter/sicherer Code enthalten ist, bzw. daß derartiger Code keine bekannten Fehler enthält. :zwinker:

Sicher. Nur ist es leichtsinnig, wenn man fremden Code völlig ungeprüft übernimmt und das Ergebnis nicht testet. :zwinker:


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:48 Uhr.

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