首页 > 系统相关 >内存映射大文件

内存映射大文件

时间:2023-05-23 15:22:05浏览次数:33  
标签:文件 读取 映射 tstream len 内存 FFile

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

标签:文件,读取,映射,tstream,len,内存,FFile
From: https://www.cnblogs.com/lucken2000/p/17425315.html

相关文章

  • Linux 挂载安装文件
    先安装后挂载1先用./configure--prefix=/usr/local/YOUR_FOLDERmake&&makeinstall安装至指定目录,方便管理2编写可执行程序、库等文件的挂载和卸载脚本SRCDIR="/usr/local/libpng"DSTDIR="/usr/local"BIN_FILE_0=bin/libpng16-configBIN_FILE_1=bin/pngfixBIN_FI......
  • Linux文件权限
    Linux的安全性Linux安全系统的核心是用户账户。每个能访问Linux系统的用户都会被分配一个唯一的用户账户。用户权限是通过创建用户时分配的用户ID(userID,UID)来跟踪的。用户在登录系统时是使用登录名(loginname)来代替UID登录的登录名是用户用来登录系统的最长8字符的字符串(字......
  • 管理文件系统
    管理文件系统Linux文件系统Linux文件系统的演进1.ext文件系统Linux操作系统最初引入的文件系统叫作扩展文件系统(extendedfilesystem,简称ext),它为Linux提供了一个基本的类Unix文件系统。ext文件系统使用i节点(inode)跟踪存储在虚拟目录中文件的相关信息。ext文件系统名称中的ex......
  • Linux查找文件内容的常用命令方法
    1.从文件内容查找匹配指定字符串的行###grep字符串filenameeg:grep2019on.log2. 从文件内容查找与正则表达式匹配的行  grep-e正则表达式 filename3.查找时不区分大小写 grep-i字符串 filename4.查找匹配的行数  grep-c字符串filenam......
  • form+iframe 实现ajax文件上传
    在做文件上传时除了传入文件外,还有附件参数,并且要求不刷新页面,之前是表单提交的方式,现在修改成ajax上传的方式,由于没有选择用插件,所以用form+iframe的方式,并且这种方式对IE8以上及主流浏览器都支持。1、首先写一个iframe<iframename="myiframe"style="display:none;"onload=......
  • https部署与docker下nginx的转发+文件获取
    进入nginx容器查看是否有etc/nginx/cert目录将https的域名证书放到cert目录下配置80和443server{#listen80;#侦听80端口listen80;#侦听443端口,用于SSLserver_name127.zyfdtsite.com;client_max_body_size1024m;#charsetkoi8-r;#acc......
  • macOS 上修改 hosts 文件后刷新DNS
    打开终端:在Launchpad中打开"终端"应用程序,或者通过在Spotlight中搜索"终端"并打开它。输入刷新命令:在终端中输入以下命令并按下回车键:sudokillall-HUPmDNSResponder这将发送刷新DNS缓存的信号。输入密码:您可能需要输入管理员密码来执行sudo命令。在输......
  • react项目在不暴露配置文件的情况下配置环境变量
    react项目在不暴露配置文件的情况下修改打包配置需要用到 react-app-rewired和customize-cra包对打包配置文件进行修改。 添加自定义环境变量有两种方法:方法1:使用dotenv-cli1、运行 yarnglobaladddotenv-cli 全局安装dotenv-cli2、在根目录下添加.env.pre文件,写入变......
  • Docker如何上传本地文件到容器目录?
    先贴上命令:dockercp本地文件路径ID全称:容器路径下面来举个例子:我要把本地电脑的mysql-connector-java-5.1.46.jar上传到Linux下Docker里面的logstash容器下的/usr/share/logstash/lib目录。1.首先使用ssh工具上传jar包到Linux的/usr/local目录:2.使用docker命令赋值到docker容器......
  • Yii2-app-advanced的配置文件优先级
    Yii2高级模板中支持多套环境配置,并且有优先级重写覆盖默认有两种dev和prod,在应用目录environments 下Yii2中的config配置文件(main.php和params.php)具有极大的灵活配置,结合配置文件的加载顺序1、使用约定 -应用目录下有config/main.php和params.php是一个全职全集......