2013-02-10 3 views
1

TFileStream을 사용하여 파일에서 동적 배열로 데이터 청크를 읽는 일부 Delphi 코드에 문제가 있습니다. 코드 작성의 원래 목적은 내용이 동일한 지 확인하기 위해 크기는 같지만 날짜 및 시간 스탬프가 다를 수있는 두 파일의 내용을 비교하는 것입니다. 이것은 쌍의 각 파일에서 별도의 동적 배열로 데이터를 읽고 한 배열의 각 바이트를 다른 바이트의 해당 바이트와 비교하여 수행됩니다.delphi TFileStream "out of memory"

코드는 TFileStream.Read를 여러 번 호출합니다. 약 75 번 통화 후 '메모리 부족'오류 메시지와 함께 프로그램이 중단됩니다.

읽는 데이터 블록의 크기가 얼마나 클지는 모르지만 오류 메시지가 나타나는 호출 수 인 것 같습니다.

코드는 필자가 비교할 필요가있는 두 개의 파일을 발견 할 때마다 필자가 작성한 함수입니다 (필자가 들어 가지 않는 이유 때문에 40 개 또는 50 개의 다른 파일 쌍이 될 수 있음) . '메모리 부족'오류는 작은 블록으로 읽히는 단일 파일이든 전체 전체에서 읽히는 여러 파일이든 상관없이 발생합니다. 그것은 오류의 결정자 인 호출 수 인 것 같습니다.

아래에 표시된 것보다 파일을 비교할 때보다 우아한 방법이 있다는 것을 알고 있지만 실제로 알고 싶은 것은 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

델파이 버전을 지정해 주시겠습니까? –

+0

죄송합니다, XE3입니다. 고마워, 크리스. – user2058600

답변

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

감사합니다, David, 빠른 답변입니다. 나는 당신의 해결책을 시도 할 것입니다. 그동안 TFileStream.Read를 반복적으로 호출하면 메모리 오버플로가 발생하는 이유는 무엇입니까? – user2058600

+3

대부분 주소 공간 조각화로 어려움을 겪고 있습니다. 이유를 모르겠다. 내 버전에서는 스택 할당 버퍼를 사용하므로 조각화가 발생하지 않습니다. –

+0

다시 한번 감사드립니다, David. – user2058600