• Delphi 中内存映射对于大文件的使用


    这篇文章主要介绍了Delphi 中内存映射对于大文件的使用的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下

    Delphi 中内存映射对于大文件的使用

    平时很少使用大文件的内存映射,碰巧遇到了这样的要求,所以把过程记录下来,当给各位一个引子吧,因为应用不算复杂,可能有考虑不到的地方,欢迎交流。

    对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如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; 
     //此处我们已10倍blocksize为一个数据块来映射,如果文件尺寸小于10倍blocksize,则直接映射整个文件长度 
     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]; 
      //取得长度,我这里做了解析,因为我存储的信息为 a:b:c 这种类型,所以以:号分隔 
      len := StrToInt(list.Strings[1]); 
      interval := StrToInt(list.Strings[2]); 
      if (i = 0) or (totallen+len >=readlen) then 
      begin 
       //如果已读取的长度加上即将要读取的长度大于 10倍blocksize,那么我们要保留之前映射末尾的内容,以便和新映射的内容合并 
       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是指向映射区域的指针 
       //注意这里第三个参数,一直设为0,这个值要根据实际情况设置 
       p := PChar(MapViewOfFile(FFile_Map,FILE_MAP_READ,0,offset,readlen)); 
      end; 
      //如果临时流中有数据,需要合并 
      if tstream.Size > 0 then 
      begin 
       //把临时流数据copy过来 
       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; 
    

      

  • 相关阅读:
    学习总结(二十六)
    学习总结(二十五)
    在知乎学习怎么参加工作
    连分数系列
    Kalman Filter
    五子棋的学习
    Dijkstra
    三等分角、化圆为方、倍立方体
    女朋友走丢数学模型
    传染病模型
  • 原文地址:https://www.cnblogs.com/marklove/p/10537947.html
Copyright © 2020-2023  润新知