2 つの TStream の内容を比較する Delphi 関数?
質問
2つある場合は比較する必要があります Tストリーム 子孫 同じ内容です。私にとって唯一興味深い結果は、ブール値の Yes / No です。
コードを書いていきます 単純なループ ストリームのコンテンツをバイトごとにチェックします。
でも私 好奇心旺盛 既存の関数があるかどうかを知るため。DelphiXE または JCL/JVCL ライブラリ内には何も見つかりませんでした。
もちろん、2 つのストリームのサイズは同じです。
解決
まさに、NickolayとしてO.あなたはブロックであなたの流れを読み、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 によって投稿された関数は優れていますが、適切に動作するには 1 つの調整が必要です。(Uwe Raabe はすでに問題を発見しました。) ループを開始する前にストリームの位置をリセットすることが重要です - または、2 つのストリームがこの関数の外部ですでにアクセスされている場合、このプロシージャはおそらく誤った 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-64kbytesのブロックを使用して、それははるかに高速になります。
。からの回答 ユーザー532231 そして マイク 99% のケースで機能しますが、 追加のチェックが必要!
TStream の子孫はほとんど何でもあり得るので、 Stream.Read が同じ量のデータを返すかどうかは保証されません, ストリームが同じ長さであっても (ストリームの子孫はデータをダウンロードすることもできるため、次のチャンクを待機している間に readed=0 バイトを返す可能性があります)。ストリームは完全に異なるメディア上に存在することもあり、一方のメディアでのみストリーム読み取りエラーが発生する可能性があります。
コードが 100% 機能するためには、これらすべてのチェックを行う必要があります。Mikeからの機能を修正しました。
たとえば、この関数を使用してストリーム 2 が Stream1 と同一でない場合に書き換える場合、すべてのエラーをチェックする必要があります。関数の結果が True の場合は何も問題ありませんが、False の場合は、ストリームが実際に異なるのか、それとも単にエラーが発生したのかを確認するのが非常に賢明です。
編集: いくつかの追加チェック、StreamsAreIdentical に基づく FilesAreIdentical 関数、および使用例を追加しました。
// 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;