平时很少使用大文件的内存映射,碰巧遇到了这样的要求,所以把过程记录下来,当给各位一个引子吧,因为应用不算复杂,可能有考虑不到的地方,欢迎交流。
对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如2G或者更多,文件流就不行了,所以要使用API的内存映射的相关方法,即使是内存映射,也不能一次映射全部文件的大小,所以必须采取分块映射,每次处理一小部分。
先来看几个函数
CreateFile :打开文件
GetFileSize : 获取文件尺寸
CreateFileMapping :创建映射
MapViewOfFile :映射文件
看MapViewOfFile的帮助,他的最后两个参数都需要是页面粒度的整数倍,一般机器的页面粒度为64k(65536字节),而我们实际操作中,一般都不是这样规矩的,任意位置,任意长度都是可能的,所以就要做一些处理。
本例的任务是从一个长度列表中(FInfoList),依次读取长度值,然后到另外一个大文件(FSourceFileName)中去顺序读取指定长度的数据,如果是小文件,这个就好办了,一次读到文件流中,然后依次读取就是了,大数对于大文件,就需要不断改变映射的位置,来取得我们想要的数据。
本例中显示先通过GetSystemInfo来获取页面粒度,然后以10倍的页面粒度为一个映射数据块,在for循环中,会判断已经读取的长度(totallen)加上即将读取的长度,是否在本次映射范围之内(10倍的页面粒度),如果在就继续读取,如果超出了,就要记下剩下的数据,然后重新映射下一块内存,并将记录下的剩余数据合并到新读取的数据中,有点绕啊(可能是我的想法太绕了),下面列出代码。
- procedure TGetDataThread.DoGetData;
- var
- FFile_Handle:THandle;
- FFile_Map:THandle;
- list:TStringList;
- p:PChar;
- i,interval:Integer;
- begin
- try
- totallen := 0;
- offset := 0;
- tstream := TMemoryStream.Create;
- stream := TMemoryStream.Create;
- list := TStringList.Create;
-
- GetSystemInfo(sysinfo);
-
- blocksize := sysinfo.dwAllocationGranularity;
-
- FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
- if 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 then Exit;
-
- if filesize div blocksize > 10 then
- readlen := 10*blocksize
- else
- readlen := filesize;
- for i := 0 to FInfoList.Count - 1 do
- begin
- list.Delimiter := ‘:‘;
- list.DelimitedText := FInfoList.Strings[i];
-
- len := StrToInt(list.Strings[1]);
- interval := StrToInt(list.Strings[2]);
- if (i = 0) or (totallen+len >=readlen) then
- begin
-
- if i > 0 then
- begin
- offset := offset + readlen;
-
- tstream.Write(p^,readlen-totallen);
- tstream.Position := 0;
- end;
-
- if filesize-offset < blocksize then
- readlen := filesize-offset;
-
-
- p := PChar(MapViewOfFile(FFile_Map,FILE_MAP_READ,0,offset,readlen));
- end;
-
- if tstream.Size > 0 then
- begin
-
- stream.CopyFrom(tstream,tstream.Size);
-
- stream.Write(p^,len-tstream.Size);
- totallen := len-tstream.Size;
-
- Inc(p,len-tstream.Size);
- tstream.Clear;
- end
- else
- begin
- stream.Write(p^,len);
- totallen := totallen + len;
- Inc(p,len);
- end;
- stream.Position := 0;
-
- stream.SaveToFile(IntToStr(i)+‘.txt‘);
- stream.Clear;
- end;
- finally
- stream.Free;
- tstream.Free;
- CloseHandle(FFile_Handle);
- CloseHandle(FFile_Map);
- end;
- end;
参考:
http://blog.csdn.net/bdmh/article/details/6369250
内存映射对于大文件的使用
原文:http://www.cnblogs.com/findumars/p/3980169.html