2013-02-10 73 views
1

我在使用TFileStream從文件讀取數據塊到動態數組的一些Delphi代碼時遇到了問題。編寫代碼的最初目的是比較具有相同大小但可能不同日期和時間戳的兩個文件的內容,以查看內容是否相同。這是通過將每對文件中的數據讀入單獨的動態數組並將一個數組的每個字節與另一個數組的相應字節進行比較來完成的。delphi TFileStream「內存不足」

該代碼對TFileStream.Read進行多次調用。大約75次調用後,程序崩潰,出現「內存不足」錯誤消息。

似乎並不重要的是讀取的數據塊有多大,它似乎是導致錯誤消息的調用次數。

該代碼是我編寫的函數,在程序遇到兩個需要比較的文件時調用其他地方(這是由於我不會介紹的原因,可能是四十或五十個不同的文件對) 。無論是以小塊讀取的單個文件,還是整個讀取的多個文件,都會發生「內存不足」錯誤。它似乎是錯誤決定因素的調用次數。

雖然我意識到可能有比下面顯示的文件比較更優雅的方式,但我真正想知道的是使用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 } 
+2

你能指定你的Delphi版本嗎? – 2013-02-10 20:43:38

+0

對不起,這是XE3。謝謝,克里斯。 – user2058600 2013-02-10 23:17:23

回答

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大小的檢查添加到此功能中,很明顯在哪裏以及如何執行此操作。

上面的例程使用堆棧分配緩衝區。相比之下,你的版本分配在堆上。也許你的版本導致堆碎片。

我意識到這並不回答你問的直接問題。但是,它確實解決了您的問題。我希望這證明有用。

+0

謝謝,大衛,快速回答。我會嘗試你的解決方案。同時,你是否知道爲什麼重複調用TFileStream.Read會導致內存溢出? – user2058600 2013-02-10 15:53:29

+3

很可能你正在遭受地址空間碎片化。不知道爲什麼。我的版本使用堆棧分配緩衝區,因此不會受到碎片影響。 – 2013-02-10 15:59:21

+0

再次感謝大衛。 – user2058600 2013-02-10 16:02:49