我需要比较两个TStream类的子类是否具有相同的内容。对我来说,唯一重要的结果是布尔型的“是”或“否”。
我将编写一个简单的循环,逐字节检查流的内容。
不过,我很好奇是否已经存在这样的函数。我没有在DelphiXE或JCL/JVCL库中找到任何相关函数。
当然,这两个流的大小是相同的!
我将编写一个简单的循环,逐字节检查流的内容。
不过,我很好奇是否已经存在这样的函数。我没有在DelphiXE或JCL/JVCL库中找到任何相关函数。
当然,这两个流的大小是相同的!
正如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;
daemon_x发布的IsIdenticalStreams函数非常出色,但需要做出一个调整才能正常工作。(Uwe Raabe已经注意到了这个问题。)在开始循环之前,重置流位置至关重要,否则如果两个流在此函数外被访问,则此过程可能会返回一个不正确的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 kbytes的块来阅读,这样会更快。
user532231和Mike的答案在99%的情况下有效,但还需要进行其他检查!
TStream的后代几乎可以是任何东西,因此,即使流长度相同(流的后代也可以下载数据,因此可能返回读取=0字节,同时等待下一块),也不能保证Stream.Read会返回相同数量的数据。流也可能完全不同的媒体,流读取错误可能只发生在其中一个上。
为了实现100%的工作代码,应进行所有这些检查。我修改了Mike的函数。
如果例如将该函数用于在Stream1与2不相同时重新编写流,则应检查所有错误。当函数结果为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;
@
符号后跟用户名(在您开始输入时会触发自动建议)来通知某人有新评论(例如,@TLama
会通知我)。由于您是帖子的所有者,因此我没有使用它,您始终会被通知,以及问题的所有者。感谢您加入Stack Overflow! - TLama(Buffer1Readed <> aBlockSize)
发生了什么? - Merlin W.if (Buffer1Readed <> Buffer2Readed)
之前退出,并且此部分 or ((Buffer1Readed <> aBlockSize) and (aStream1.Position < aStream1.Size))
允许仅在流结束时读取小于 aBlock 大小的数据。这实际上就是你所要求的 ;) - david