Delphi에서 대용량 파일에 대한 메모리 매핑 사용
대용량 파일의 메모리 매핑을 거의 사용하지 않는데, 이런 요구사항이 있어서 소개하기 위해 과정을 기록해 두었습니다.
일부 작은 파일의 경우 일반 파일 스트림을 사용하여 문제를 해결할 수 있지만 2G 이상과 같은 매우 큰 파일의 경우 파일 스트림이 작동하지 않으므로 API의 메모리 매핑 관련 방법을 사용해야 합니다. 메모리 매핑인 경우 전체 파일 크기를 한 번에 매핑할 수 없으므로 매핑은 한 번에 작은 부분을 처리하면서 청크로 이루어져야 합니다.
먼저 몇 가지 기능을 살펴보겠습니다.
CreateFile: 파일 열기
GetFileSize: 파일 크기 가져오기
CreateFileMapping: 매핑 생성
MapViewOfFile: 매핑 파일
MapViewOfFile의 도움말을 보면 마지막 두 매개변수는 페이지 세분성의 정수배여야 합니다. 일반적으로 시스템의 페이지 세분성은 64k(65536바이트)입니다. 그러나 실제 작업에서는 일반적으로 그렇지 않습니다. . 어떤 위치, 어떠한 길이도 가능하므로 약간의 가공이 필요합니다.
이 예제의 작업은 길이 목록(FInfoList)에서 길이 값을 순차적으로 읽은 다음 다른 대용량 파일(FSourceFileName)에서 지정된 길이의 데이터를 순차적으로 읽는 것입니다. 파일 스트림을 한 번 읽은 다음 순서대로 읽으면 됩니다. 대용량 파일의 경우 원하는 데이터를 얻으려면 매핑 위치를 지속적으로 변경해야 합니다.
이 예에서는 먼저 GetSystemInfo를 통해 페이지 단위를 얻은 다음 해당 페이지 단위의 10배를 매핑된 데이터 블록으로 사용하는 경우 for 루프에서 읽은 길이(totallen)에 읽을 길이를 더한 값인지 판단합니다. 이 시간 안에 있습니다 매핑 범위(페이지 단위의 10배) 이내라면 계속해서 읽어야 하며, 이를 초과하면 남은 데이터를 기록하고, 다음 메모리 블록을 다시 매핑하고, 기록된 남은 데이터를 병합해야 한다. 얻은 데이터가 약간 복잡합니다(내 생각이 너무 복잡할 수도 있음).
코드는 아래에 나열되어 있습니다.
절차 TGetDataThread.DoGetData; var FFile_Map:THandle; i,interval:Integer; offset := TMemoryStream.Create; TMemoryStream.Create; 목록 := TStringList.Create; //시스템 정보 가져오기 GetSystemInfo(sysinfo); //페이지 할당 세분성 blocksize := sysinfo.dwAllocationGranularity; //파일 열기 FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); FFile_Handle = INVALID_HANDLE_VALUE then Exit; //파일 크기 가져오기 filesize := GetFileSize(FFile_Handle,nil); //매핑 생성 FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil); if FFile_Map = 0; //여기서는 블록 크기의 10배를 하나의 데이터 블록에 매핑했습니다. 파일 크기가 블록 크기의 10배보다 작으면 파일 크기 div 블록 크기 > 10이면 전체 파일 길이가 직접 매핑됩니다. 그러면 readlen := 10*blocksize else readlen := filesize; for i := 0에서 FInfoList.Count - 1 시작 list.Delimiter := ':'; list.DelimitedText := FInfoList.Strings[i]; //길이를 구합니다. 저장한 정보가 a:b:c 유형이므로 여기에서 분석을 수행했습니다. 따라서 len은 ::= StrToInt(list.Strings[1]) Interval := StrToInt(list)로 구분됩니다. .Strings[2]); if (i = 0) 또는 (totallen+len >=readlen) 시작 //읽은 길이에 읽을 길이를 더한 값이 블록 크기의 10배보다 큰 경우 i > 0인 경우 새 매핑 콘텐츠와 병합하기 위해 이전 매핑 끝의 콘텐츠를 유지한 다음 시작해야 합니다. offset := offset + readlen ; //임시 스트림에 쓰기 tstream.Write(p^,readlen-totallen); tstream.Position := 0 end; 세분성, 파일 크기 오프셋 < 블록 크기인 경우 나머지 길이를 직접 매핑합니다. readlen := filesize-offset; //매핑, p는 매핑 영역에 대한 포인터입니다. //여기서 세 번째 매개변수는 항상 0으로 설정됩니다. 이 값은 실제 상황에 따라 설정되어야 합니다. p:= PChar(MapViewOfFile( FFile_Map,FILE_MAP_READ, 0,offset,readlen)); end; //임시 스트림에 데이터가 있는 경우 tstream.Size > 0이면 병합해야 합니다. //임시 스트림 데이터를 stream.CopyFrom(tstream,tstream.Size)에 복사합니다. //그런 다음 마지막에 새 데이터를 쓰고 병합하여 완성합니다. stream.Write(p^,len-tstream.Size) totallen := len - tstream.Size; //다음 데이터의 시작 부분을 가리키도록 포인터 위치를 이동합니다. Inc(p,len-tstream.Size) tstream.Clear; end else start stream.Write(p^,len); 토탈렌 := 토탈렌 + len; Inc(p,len); end; stream.Position := 0; //스트림을 파일에 저장합니다.SaveToFile(IntToStr(i)+'.txt'); 무료 ; tstream.Free; CloseHandle(FFile_Map);
궁금한 점이 있으면 메시지를 남기거나 이 사이트의 커뮤니티에 가서 소통하고 토론하세요. 읽어주셔서 감사합니다. 이 사이트를 지원해 주셔서 감사합니다.