zoukankan      html  css  js  c++  java
  • 内存映射对于大文件的使用

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

    对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如2G或者更多,文件流就不行了,所以要使用API的内存映射的相关方法,即使是内存映射,也不能一次映射全部文件的大小,所以必须采取分块映射,每次处理一小部分。

    先来看几个函数
    CreateFile :打开文件
    GetFileSize : 获取文件尺寸
    CreateFileMapping :创建映射
    MapViewOfFile :映射文件

    看MapViewOfFile的帮助,他的最后两个参数都需要是页面粒度的整数倍,一般机器的页面粒度为64k(65536字节),而我们实际操作中,一般都不是这样规矩的,任意位置,任意长度都是可能的,所以就要做一些处理。
    本例的任务是从一个长度列表中(FInfoList),依次读取长度值,然后到另外一个大文件(FSourceFileName)中去顺序读取指定长度的数据,如果是小文件,这个就好办了,一次读到文件流中,然后依次读取就是了,大数对于大文件,就需要不断改变映射的位置,来取得我们想要的数据。
    本例中显示先通过GetSystemInfo来获取页面粒度,然后以10倍的页面粒度为一个映射数据块,在for循环中,会判断已经读取的长度(totallen)加上即将读取的长度,是否在本次映射范围之内(10倍的页面粒度),如果在就继续读取,如果超出了,就要记下剩下的数据,然后重新映射下一块内存,并将记录下的剩余数据合并到新读取的数据中,有点绕啊(可能是我的想法太绕了),下面列出代码。 

    [delphi] view plaincopy
     
    1. procedure TGetDataThread.DoGetData;  
    2. var  
    3.   FFile_Handle:THandle;  
    4.   FFile_Map:THandle;  
    5.   list:TStringList;  
    6.   p:PChar;  
    7.   i,interval:Integer;  
    8. begin  
    9.   try  
    10.   totallen := 0;  
    11.   offset := 0;  
    12.   tstream := TMemoryStream.Create;  
    13.   stream := TMemoryStream.Create;  
    14.   list := TStringList.Create;  
    15.   //获取系统信息  
    16.   GetSystemInfo(sysinfo);  
    17.   //页面分配粒度大小  
    18.   blocksize := sysinfo.dwAllocationGranularity;  
    19.   //打开文件  
    20.   FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);  
    21.   if FFile_Handle = INVALID_HANDLE_VALUE then Exit;  
    22.   //获取文件尺寸  
    23.   filesize := GetFileSize(FFile_Handle,nil);  
    24.   //创建映射  
    25.   FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil);  
    26.   if FFile_Map = then Exit;  
    27.   //此处我们已10倍blocksize为一个数据块来映射,如果文件尺寸小于10倍blocksize,则直接映射整个文件长度  
    28.   if filesize div blocksize > 10 then  
    29.     readlen := 10*blocksize  
    30.   else  
    31.     readlen := filesize;  
    32.   for i := to FInfoList.Count - do  
    33.   begin  
    34.     list.Delimiter := ':';  
    35.     list.DelimitedText := FInfoList.Strings[i];  
    36.     //取得长度,我这里做了解析,因为我存储的信息为 a:b:c 这种类型,所以以:号分隔  
    37.     len := StrToInt(list.Strings[1]);  
    38.     interval := StrToInt(list.Strings[2]);  
    39.     if (i = 0) or (totallen+len >=readlen) then  
    40.     begin  
    41.       //如果已读取的长度加上即将要读取的长度大于 10倍blocksize,那么我们要保留之前映射末尾的内容,以便和新映射的内容合并  
    42.       if i > then  
    43.       begin  
    44.         offset := offset + readlen;  
    45.         //写入临时流  
    46.         tstream.Write(p^,readlen-totallen);  
    47.         tstream.Position := 0;  
    48.       end;  
    49.       //如果未读取的数据长度已经不够一个分配粒度,那么就直接映射剩下的长度  
    50.       if filesize-offset < blocksize then  
    51.         readlen := filesize-offset;  
    52.       //映射,p是指向映射区域的指针  
    53.       //注意这里第三个参数,一直设为0,这个值要根据实际情况设置  
    54.       p := PChar(MapViewOfFile(FFile_Map,FILE_MAP_READ,0,offset,readlen));  
    55.     end;  
    56.     //如果临时流中有数据,需要合并  
    57.     if tstream.Size > then  
    58.     begin  
    59.       //把临时流数据copy过来  
    60.       stream.CopyFrom(tstream,tstream.Size);  
    61.       //然后在末尾写入新数据,合并完成  
    62.       stream.Write(p^,len-tstream.Size);  
    63.       totallen := len-tstream.Size;  
    64.       //移动指针的位置,指向下一个数据的开始  
    65.       Inc(p,len-tstream.Size);  
    66.       tstream.Clear;  
    67.     end  
    68.     else  
    69.     begin  
    70.       stream.Write(p^,len);  
    71.       totallen := totallen + len;  
    72.       Inc(p,len);  
    73.     end;  
    74.     stream.Position := 0;  
    75.     //将流保存成文件  
    76.     stream.SaveToFile(IntToStr(i)+'.txt');  
    77.     stream.Clear;  
    78.   end;  
    79.   finally  
    80.     stream.Free;  
    81.     tstream.Free;  
    82.     CloseHandle(FFile_Handle);  
    83.     CloseHandle(FFile_Map);  
    84.   end;  
    85. end;  
     

     参考:

    http://blog.csdn.net/bdmh/article/details/6369250

  • 相关阅读:
    CUDA[2] Hello,World
    mysql操作
    virsh 连接虚拟机 (vnc 或 控制台)
    ssh访问流程
    使用ceph-deploy进行ceph安装
    openstack 的horizon的结构
    django 后台格式化数据库查询出的日期
    web 应用的部署
    工具
    python性能优化
  • 原文地址:https://www.cnblogs.com/findumars/p/3980169.html
Copyright © 2011-2022 走看看