1

TFileStream を使用してファイルから動的配列にデータのチャンクを読み取る Delphi コードで問題が発生しています。コードを作成する際の本来の目的は、サイズは同じで日付とタイム スタンプが異なる可能性がある 2 つのファイルの内容を比較して、内容が同じかどうかを確認することです。これは、ペアの各ファイルからデータを個別の動的配列に読み取り、一方の配列の各バイトを他方の対応するバイトと比較することによって行われます。

このコードは、TFileStream.Read を複数回呼び出します。約 75 回の呼び出しの後、プログラムは「メモリ不足」エラー メッセージでクラッシュします。

読み取られるデータのブロックの大きさは問題ではないようです。エラー メッセージが表示されるのは呼び出しの数のようです。

コードは私が書いた関数で、プログラムが比較する必要がある 2 つのファイルに遭遇するたびに別の場所で呼び出されます (ここでは説明しませんが、40 から 50 の異なるファイル ペアである可能性があります)。「メモリ不足」エラーは、小さなブロックで読み取られている 1 つのファイルであるか、全体が読み取られている複数のファイルであるかに関係なく発生します。エラーの決定要因は呼び出し回数のようです。

ファイルの比較を行うには、以下に示すよりも洗練された方法があるかもしれないことは理解していますが、本当に知りたいのは、TFileStream および/または SetLength 呼び出しの使用に問題があることです。メモリの問題。(コードに示されているように)呼び出しのたびにメモリを解放しようとしましたが、違いはないようです。

誰かが何がうまくいかないのか説明できれば幸いです。

function Compare_file_contents(SPN,TPN : String; SourceFileSize : int64) : boolean;

var

  SF                : TFileStream; //First file of pair for comparison
  TF                : TFileStream; //Second file of pair
  SourceArray       : TBytes; // Buffer array to receive first file data
  TargetArray       : TBytes; //Buffer array to receive second file data
  ArrayLength       : int64; //Length of dynamic array
  Position          : int64; //Position within files to start each block of data read
  TestPosition      : int64; //Position within dynamic arrays to compare each byte
  MaxArrayLength    : integer; //Maximum size for the buffer arrays
  LastRun           : Boolean; //End first repeat loop

begin

{ The comparison has an arbitrary upper boundary of 100 MB to avoid slowing the
  the overall program. The main files bigger than this will be *.pst files that
  will most likely have new dates every time the program is run, so it will take
  about the same time to copy the files as it does to read and compare them, and
  it will have to be done every time.

  The function terminates when it is confirmed that the files are not the same.
  If the source file is bigger than 100 MB, it is simply assumed that they are
  not identical, thus Result = False. Also, LongInt integers (=integers) have
  a range of -2147483648..2147483647, so files bigger than 2 GB will have
  overflowed to a negative number. Hence the check to see if the file size is
  less than zero.

  The outer repeat ... until loop terminates on LastRun, but LastRun should only
  be set if SecondLastRun is True, because it will skip the final comparisons in
  the inner repeat ... until loop otherwise. }

  Result := True;
  LastRun := False;
  MaxArrayLength := 1024*1024;
  if (SourceFileSize > 100*1024*1024) or (SourceFileSize < 0) then Result := False
    else
      begin

{ The comparison is done by using TFileStream to open and read the data from
  the source and target files as bytes to dynamic arrays (TBytes). Then a repeat
  loop is used to compare individual bytes until a difference is found or all
  of the information has been compared. If a difference is found, Result is
  set to False. }

    if SourceFileSize > MaxArrayLength then ArrayLength := MaxArrayLength
      else ArrayLength := SourceFileSize;
    SF := TFileStream.Create(SPN,fmOpenRead);
    TF := TFileStream.Create(TPN,fmOpenRead);
    Position := 0;
    SetLength(SourceArray,ArrayLength);
    SetLength(TargetArray,ArrayLength);
    try
      SF.Read(SourceArray,ArrayLength);
      TF.Read(TargetArray,ArrayLength);
      Position := SF.Position;
    finally
      SF.Free;
      TF.Free;
    end;
      repeat
      TestPosition := 0;
        repeat
          if SourceArray[TestPosition] <> TargetArray[TestPosition] then
            Result := False;
          Inc(TestPosition);
        until (Result = False) or (TestPosition = ArrayLength);
        if SourceFileSize > Position then
          begin
            if SourceFileSize - Position - MaxArrayLength > 0 then
              ArrayLength := MaxArrayLength
              else ArrayLength := SourceFileSize - Position;
            SF := TFileStream.Create(SPN,fmOpenRead);
            TF := TFileStream.Create(TPN,fmOpenRead);
            SF.Position := Position;
            TF.Position := Position;
            try
              SF.Read(SourceArray,ArrayLength);
              TF.Read(TargetArray,ArrayLength);
              Position := SF.Position;
            finally
              SF.Free;
              TF.Free;
            end;
        end else LastRun := True;
      until (Result = False) or LastRun;
      Finalize(SourceArray);
      Finalize(TargetArray);
  end;
end; { Compare_file_contents }
4

1 に答える 1

6

このルーチンは、必要以上に複雑なようです。デバッグしようとするのではなく、ストリームを比較するルーチンを提供します。

function StreamsEqual(Stream1, Stream2: TStream): Boolean;
const
  OneKB = 1024;
var
  Buffer1, Buffer2: array [0..4*OneKB-1] of Byte;
  SavePos1, SavePos2: Int64;
  Count: Int64;
  N: Integer;
begin
  if Stream1.Size<>Stream2.Size then begin
    Result := False;
    exit;
  end;

  SavePos1 := Stream1.Position;
  SavePos2 := Stream2.Position;
  Try
    Stream1.Position := 0;
    Stream2.Position := 0;

    Count := Stream1.Size;
    while Count <> 0 do begin
      N := Min(SizeOf(Buffer1), Count);
      Stream1.ReadBuffer(Buffer1, N);
      Stream2.ReadBuffer(Buffer2, N);
      if not CompareMem(@Buffer1, @Buffer2, N) then begin
        Result := False;
        exit;
      end;
      dec(Count, N);
    end;
    Result := True;
  Finally
    Stream1.Position := SavePos1;
    Stream2.Position := SavePos2;
  End;
end;

この関数に 100MB のサイズ チェックを追加する場合、どこでどのように行うかは明らかです。

上記のルーチンは、スタックに割り当てられたバッファーを使用します。対照的に、バージョンはヒープに割り当てます。おそらく、あなたのバージョンはヒープの断片化につながります。

これはあなたが尋ねた直接の質問に答えていないことを認識しています. ただし、問題は解決します。これが役立つことを願っています。

于 2013-02-10T15:49:59.380 に答える