Delphi función de comparar el contenido de dos TStream?
Pregunta
necesito para comparar si dos TStream descendiente tienen el mismo contenido . El resultado sólo es interesante para mí es el booleano Sí / No.
Voy a código de una simple bucle comprobación de bytes a partir del byte de las corrientes de contenido.
Pero estoy curiosa para saber si hay una función ya existente. No he encontrado ninguna DelphiXE dentro o JCL / libs JVCL.
Por supuesto, las dos corrientes tienen el mismo tamaño!
Solución
Exactamente, como Nickolay O. dijo que debe leer su secuencia en bloques y usar CompareMem. Aquí hay un ejemplo (incluyendo prueba de tamaño) ...
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;
Otros consejos
La IsIdenticalStreams Función Publicado por daemon_x es excelente - pero necesita un ajuste para que funcione correctamente. (Uwe Raabe captó el tema ya.) Es crítico que restablecer las posiciones de la corriente antes de comenzar el bucle - o este procedimiento probablemente devolver un VERDADERO incorrecto si las dos corrientes que ya se ha accedido, fuera de esta función.
Esta es la solución final que funciona todo el tiempo. Yo sólo cambió el nombre de la función para satisfacer mis convenciones de nomenclatura. Gracias daemon_x para la solución 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;
No hay tal función incorporada. Sólo hay una cosa que puedo recomendar -. No leer byte a byte, pero utilizando bloques de 16-64kbytes, que sería mucho más rápido
Las respuestas de user532231 y Mike están trabajando en el 99% de los casos, pero hay comprobaciones adicionales que se harán
Los descendientes de TStream puede ser casi cualquier cosa, por lo que no está garantizado que Stream.Read volverá misma cantidad de datos , incluso si las corrientes son de la misma longitud (descendiente corriente puede también descargar los datos, por lo puede devolver leido = 0 bytes, mientras espera para el próximo trozo). Corrientes pueden ser también de completelly diferentes medios de comunicación y flujo de error de lectura podrían producirse en una sola.
En el código de trabajo 100% todos debería convertirse en estas comprobaciones. He modificado la función de Mike.
Si se utiliza esta función, por ejemplo, para volver a escribir la corriente 2, si no idéntico, al TRANSMISIÓN 1, todos los errores deben ser revisados. Cuando resultado de la función es verdadera, todo es aceptable, pero si es falso, sería muy inteligente para comprobar si los flujos son realmente diferentes o simplemente ocurrió algún error.
Editado:. Añadido algunas comprobaciones adicionales, la función FilesAreIdentical basado en StreamsAreIdentical y ejemplo de uso
// 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;