zoukankan      html  css  js  c++  java
  • 内存映射大文件

    对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如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;  

    http://www.cnblogs.com/hnxxcxg/archive/2012/11/04/2753265.html

  • 相关阅读:
    OPPO R9sPlus MIFlash线刷TWRP Recovery ROOT详细教程
    OPPO R11 R11plus系列 解锁BootLoader ROOT Xposed 你的手机你做主
    努比亚(nubia) M2青春版 NX573J 解锁BootLoader 并进入临时recovery ROOT
    华为 荣耀 等手机解锁BootLoader
    青橙 M4 解锁BootLoader 并刷入recovery ROOT
    程序员修炼之道阅读笔03
    冲刺8
    典型用户模板分析
    学习进度八
    冲刺7
  • 原文地址:https://www.cnblogs.com/findumars/p/5237001.html
Copyright © 2011-2022 走看看