2013-02-10 43 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