Функция 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;