fonction Delphi comparer le contenu de deux TStream?
Question
Je dois comparer si deux TStream descendant ont le même contenu . Le seul résultat intéressant pour moi est le booléen Oui / Non.
Je vais coder un simple boucle vérification octet après octet
de la teneur en eau.Mais je suis curieux pour savoir s'il y a une fonction déjà existante. Je n'ai pas trouvé dans libs DelphiXE ou JCL / JVCL.
Bien sûr, les deux cours d'eau ont la même taille!
La solution
Exactement, comme Nickolay O. dit que vous devriez lire votre flux en blocs et utiliser CompareMem. Voici un exemple (y compris le test de taille) ...
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;
Autres conseils
La fonction IsIdenticalStreams Publié par daemon_x est excellent - mais a besoin d'un ajustement pour fonctionner correctement. (Uwe Raabe a pris la question déjà.) Il est essentiel que vous réinitialiser les positions de flux avant de commencer la boucle - ou cette procédure retournerez probablement un VRAI incorrect si les deux cours d'eau étaient déjà accessibles en dehors de cette fonction.
Ceci est la solution finale qui fonctionne à chaque fois. Je viens retitré la fonction en fonction de mes conventions de nommage. Merci daemon_x pour la solution élégante.
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;
Il n'y a pas une telle fonction intégrée. Une seule chose que je peux recommander -. Pas lu octet par octet, mais en utilisant des blocs de 16-64kbytes, ce serait beaucoup plus rapide
Les réponses de user532231 et Mike travaillent dans 99% des cas, mais il y a contrôles supplémentaires à effectuer
Les descendants de TStream peuvent être presque tout, si il est pas garanti que Stream.Read retourne même quantité de données , même si les flux sont de la même longueur (descendant de flux peut également télécharger des données, donc peut retourner readed = 0 octets, en attendant morceau suivant). Les flux peuvent être également sur des supports différents et completelly erreur de lecture de flux peuvent se produire sur un seul.
Pour le code de 100% tous ces contrôles devraient être effectués. J'ai modifié la fonction de Mike.
Si cette fonction est utilisée par exemple pour réécrire le flux 2 sinon identique à Stream1, il faut vérifier toutes les erreurs. Lorsque résultat de la fonction est vrai, everthing est ok, mais si elle est fausse, il serait très intelligent pour vérifier si Streams sont réellement différents ou tout simplement une erreur est survenue.
Modifié:. Ajout des contrôles supplémentaires, la fonction FilesAreIdentical basée sur StreamsAreIdentical et exemple d'utilisation
// 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;