Функция Delphi сравнивает содержимое двух TStream?
Мне нужно сравнить если два TStream потомок содержание. Единственным интересным результатом для меня является логическое Да / нет.
Я в коде простые петли проверка байта за байтом содержимого потоков.
но я любопытный чтобы узнать, есть ли уже существующая функция. Я не нашел никаких внутри delphixe или JCL/jvcl libs.
конечно, два потока имеют то же самое размер !
4 ответов
точно, как сказал Николай О., Вы должны читать свой поток в блоках и использовать CompareMem. Вот пример (включая тест размера) ...
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;
на IsIdenticalStreams функция, размещенная daemon_x, превосходна , но для правильной работы требуется одна настройка. (Уве Раабе уже понял, в чем дело.) Это важно, чтобы вы сбросили позиции потока перед запуском цикла - или эта процедура, вероятно, вернет неверное значение TRUE, если два потока уже были доступны вне этой функции.
это окончательное решение, которое работает каждый раз. Я просто переименовал функцию в соответствии с моим соглашение об именах. Спасибо daemon_x за элегантное решение.
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;
нет такой встроенной функции. Только одно могу порекомендовать-читать не байт-в-байт, а используя блоки по 16-64кбайт, что было бы намного быстрее.
ответы user532231 и Майк работаем в 99% случаев, но есть дополнительные проверки!
потомками TStream может быть практически все, поэтому это не гарантирует, что поток.Read вернет столько же данных, даже если потоки имеют одинаковую длину (потоковый потомок также может загружать данные, поэтому может возвращать readed=0 байт, ожидая следующего фрагмента). Потоки могут быть также полностью различные носители и поток ошибка чтения может произойти только на одном.
для 100% рабочего кода все эти проверки должны быть сделаны. Я изменил функцию от Майка.
если эта функция используется, например, для перезаписи потока 2, если она не идентична Stream1, все ошибки должны быть проверены. Когда результат функции True, everthing в порядке, но если это False, было бы очень умно проверить, действительно ли потоки отличаются или просто какая-то ошибка происходить.
редактировать: добавлены некоторые дополнительные проверки, функция FilesAreIdentical на основе StreamsAreIdentical и пример использования.
// 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;