دلفي وظيفة مقارنة محتوى اثنين من tstream؟
سؤال
أحتاج إلى المقارنة إذا اثنين tstream تنازلي لديك نفس المحتوى. النتيجة الوحيدة المثيرة للاهتمام بالنسبة لي هي نعم / لا.
انا ذاهب الى رمز أ حلقة بسيطة التحقق من البايت بعد بايت محتوى التدفقات.
لكن أنا فضولي لمعرفة ما إذا كانت هناك وظيفة موجودة بالفعل. لم أجد أي داخل Delphixe أو JCL/JVCL LIBS.
بالطبع ، فإن الجداول لها نفس الحجم!
المحلول
بالضبط ، كما قال Nickolay O. يجب عليك قراءة الدفق الخاص بك في الكتل واستخدام المقارنة. هنا مثال (بما في ذلك اختبار الحجم) ...
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 ممتازة - ولكنها تحتاج إلى تعديل واحد للعمل بشكل صحيح. (اشتعلت Uwe Raabe القضية بالفعل.) إنها كذلك من الأهمية بمكان إعادة تعيين مواضع الدفق قبل بدء الحلقة - أو من المحتمل أن يعيد هذا الإجراء صحيحًا غير صحيح إذا تم الوصول إلى الجداول بالفعل خارج هذه الوظيفة.
هذا هو الحل النهائي الذي يعمل في كل مرة. أنا فقط أعيد تسمية الوظيفة لتناسب اتفاقيات التسمية الخاصة بي. شكرا لك 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 أي شيء تقريبًا ، لذلك ليس مضمونًا أن يقوم Stream.Read بإرجاع نفس مقدار البيانات, ، حتى إذا كانت التدفقات من نفس الطول (يمكن أيضًا لتنسيق دفق تنزيل البيانات ، لذلك قد يتم إرجاعها المقروء = 0 بايت ، أثناء انتظار الجزء التالي). يمكن أن تكون التدفقات أيضًا على وسائط مختلفة ، ويمكن أن يحدث خطأ في قراءة الدفق في واحدة فقط.
بالنسبة إلى رمز العمل 100 ٪ ، يجب إجراء جميع هذه الشيكات. لقد قمت بتعديل الوظيفة من مايك.
إذا تم استخدام هذه الوظيفة على سبيل المثال لإعادة كتابة الدفق 2 إذا لم تكن متطابقة مع Stream1 ، فيجب فحص جميع الأخطاء. عندما تكون نتيجة الوظيفة صحيحة ، يكون كل شيء على ما يرام ، ولكن إذا كان هذا خطأ ، فسيكون من الذكاء للغاية التحقق مما إذا كانت التدفقات مختلفة فعليًا أو تحدث بعض الأخطاء.
تحرير: تمت إضافة بعض الاختبارات الإضافية ، وظيفة FilesAreidentical استنادًا إلى مثال StreamSareActionIncitical و Usage.
// 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;