سؤال

أحتاج إلى المقارنة إذا اثنين 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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top