funzione di Delphi confrontando il contenuto di due TStream?
Domanda
Ho bisogno di confrontare se due TStream discendente hanno lo stesso contenuto . Il risultato solo interessante per me è il booleano Sì / No.
ho intenzione di codice di un semplice ciclo il controllo di byte dopo byte i flussi di contenuti.
Ma sono curiosi per sapere se v'è una funzione già esistente. Non ho trovato alcun all'interno DelphiXE o JCL / libs JVCL.
Naturalmente, i due corsi d'acqua hanno la stessa dimensione!
Soluzione
Esattamente, come Nickolay O. detto si dovrebbe leggere il vostro flusso di blocchi e utilizzare CompareMem. Ecco un esempio (tra cui test di dimensioni) ...
function IsIdenticalStreams(Source, Destination: TStream): boolean;
const Block_Size = 4096;
var Buffer_1: array[0..Block_Size-1] of byte;
Buffer_2: array[0..Block_Size-1] of byte;
Buffer_Length: integer;
begin
Result := False;
if Source.Size <> Destination.Size then
Exit;
while Source.Position < Source.Size do
begin
Buffer_Length := Source.Read(Buffer_1, Block_Size);
Destination.Read(Buffer_2, Block_Size);
if not CompareMem(@Buffer_1, @Buffer_2, Buffer_Length) then
Exit;
end;
Result := True;
end;
Altri suggerimenti
Il IsIdenticalStreams Funzione inviato da daemon_x è eccellente - ma ha bisogno di un aggiustamento per funzionare correttamente. (Uwe Raabe catturato la questione già.) E ' critico che si ripristinano le posizioni di flusso prima di iniziare il ciclo - o questa procedura sarà probabilmente restituire un TRUE non corretto se i due flussi erano già accessibili al di fuori di questa funzione.
Questa è la soluzione finale che funziona ogni volta. Ho appena rinominato la funzione per soddisfare le mie convenzioni di denominazione. Grazie daemon_x per la soluzione elegante.
function StreamsAreIdentical(Stream1, Stream2: TStream): boolean;
const
Block_Size = 4096;
var
Buffer_1: array[0..Block_Size-1] of byte;
Buffer_2: array[0..Block_Size-1] of byte;
Buffer_Length: integer;
begin
Result := False;
if Stream1.Size <> Stream2.Size then exit;
// These two added lines are critical for proper operation
Stream1.Position := 0;
Stream2.Position := 0;
while Stream1.Position < Stream1.Size do
begin
Buffer_Length := Stream1.Read(Buffer_1, Block_Size);
Stream2.Read(Buffer_2, Block_Size);
if not CompareMem(@Buffer_1, @Buffer_2, Buffer_Length) then exit;
end;
Result := True;
end;
Non esiste una funzione built-in. Solo una cosa che posso consigliare -. Non leggere byte per byte, ma usando blocchi di 16-64kbytes, che sarebbe molto più veloce
Le risposte di user532231 e Mike stanno lavorando in 99% dei casi, ma ci sono ulteriori controlli da effettuare
I discendenti di TStream può essere quasi qualsiasi cosa, quindi non è garantito che Stream.Read tornerà stessa quantità di dati , anche se i flussi sono della stessa lunghezza (flusso discendente è anche possibile scaricare i dati, in modo da possono trasferire readed = 0 byte, in attesa chunk successivo). Flussi possono essere anche completelly diversi media e flusso di errore di lettura potrebbero verificarsi su uno solo.
Per il codice a lavorare al 100% tutti questi controlli dovrebbe essere fatto. Ho modificato la funzione di Mike.
Se si utilizza questa funzione ad esempio di riscrivere flusso 2 se non identici a Stream1, tutti gli errori dovrebbe essere controllato. Quando risultato della funzione è vero, everthing è ok, ma se è falso, sarebbe molto intelligente per controllare se Streams sono in realtà diverse o semplicemente un po 'di errore.
Modificato:. aggiunti alcuni controlli aggiuntivi, la funzione FilesAreIdentical sulla base di StreamsAreIdentical e esempio di utilizzo
// Usage example
var lError: Integer;
...
if FilesAreIdentical(lError, 'file1.ext', 'file2.ext')
then Memo1.Lines.Append('Files are identical.')
else case lError of
0: Memo1.Lines.Append('Files are NOT identical!');
1: Memo1.Lines.Append('Files opened, stream read exception raised!');
2: Memo1.Lines.Append('File does not exist!');
3: Memo1.Lines.Append('File open exception raised!');
end; // case
...
// StreamAreIdentical
function StreamsAreIdentical(var aError: Integer;
const aStream1, aStream2: TStream;
const aBlockSize: Integer = 4096): Boolean;
var
lBuffer1: array of byte;
lBuffer2: array of byte;
lBuffer1Readed,
lBuffer2Readed,
lBlockSize: integer;
begin
Result:=False;
aError:=0;
try
if aStream1.Size <> aStream2.Size
then Exit;
aStream1.Position:=0;
aStream2.Position:=0;
if aBlockSize>0
then lBlockSize:=aBlockSize
else lBlockSize:=4096;
SetLength(lBuffer1, lBlockSize);
SetLength(lBuffer2, lBlockSize);
lBuffer1Readed:=1; // just for entering while
while (lBuffer1Readed > 0) and (aStream1.Position < aStream1.Size) do
begin
lBuffer1Readed := aStream1.Read(lBuffer1[0], lBlockSize);
lBuffer2Readed := aStream2.Read(lBuffer2[0], lBlockSize);
if (lBuffer1Readed <> lBuffer2Readed) or ((lBuffer1Readed <> lBlockSize) and (aStream1.Position < aStream1.Size))
then Exit;
if not CompareMem(@lBuffer1[0], @lBuffer2[0], lBuffer1Readed)
then Exit;
end; // while
Result:=True;
except
aError:=1; // stream read exception
end;
end;
// FilesAreIdentical using function StreamsAreIdentical
function FilesAreIdentical(var aError: Integer;
const aFileName1, aFileName2: String;
const aBlockSize: Integer = 4096): Boolean;
var lFileStream1,
lFilestream2: TFileStream;
begin
Result:=False;
try
if not (FileExists(aFileName1) and FileExists(aFileName2))
then begin
aError:=2; // file not found
Exit;
end;
lFileStream1:=nil;
lFileStream2:=nil;
try
lFileStream1:=TfileStream.Create(aFileName1, fmOpenRead or fmShareDenyNone);
lFileStream2:=TFileStream.Create(aFileName2, fmOpenRead or fmShareDenyNone);
result:=StreamsAreIdentical(aError, lFileStream1, lFileStream2, aBlockSize);
finally
if lFileStream2<>nil
then lFileStream2.Free;
if lFileStream1<>nil
then lFileStream1.Free;
end; // finally
except
aError:=3; // file open exception
end; // except
end;