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

发布时间 - 2026-01-11 03:05:07    点击率:

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; 

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# Delphi  # 内存映射  # 内存映射的使用方法  # 内存映射大文件使用方法  # Delphi 用DLL实现插件的简单实例  # Delphi 根据字符串找到函数并执行的实例  # Delphi提取PDF文本实例  # Delphi XE5 为Android应用制作签名的方法(图文)  # ListView 百分比进度条(delphi版)  # Delphi实现截屏存盘的方法  # Delphi实现窗体感知鼠标滑过并自动隐藏与显示窗口的方法  # Delphi 实现软件自动升级的功能  # 大文件  # 都是  # 本例  # 几个  # 很好  # 都不  # 是这样  # 如有  # 就好  # 中有  # 设为  # 是从  # 希望能  # 要做  # 并将  # 能有  # 即使是  # 实际情况  # 另外一个  # 中去 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 香港服务器选型指南:免备案配置与高效建站方案解析  高防服务器租用如何选择配置与防御等级?  手机网站制作平台,手机靓号代理商怎么制作属于自己的手机靓号网站?  今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  如何在阿里云高效完成企业建站全流程?  Windows Hello人脸识别突然无法使用  Laravel全局作用域是什么_Laravel Eloquent Global Scopes应用指南  Laravel如何生成API文档?(Swagger/OpenAPI教程)  如何有效防御Web建站篡改攻击?  三星网站视频制作教程下载,三星w23网页如何全屏?  UC浏览器如何设置启动页 UC浏览器启动页设置方法  如何确认建站备案号应放置的具体位置?  如何快速生成橙子建站落地页链接?  如何快速重置建站主机并恢复默认配置?  Laravel怎么使用Intervention Image库处理图片上传和缩放  免费网站制作appp,免费制作app哪个平台好?  邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?  html5audio标签播放结束怎么触发事件_onended回调方法【教程】  Laravel如何使用查询构建器?(Query Builder高级用法)  javascript日期怎么处理_如何格式化输出  如何用已有域名快速搭建网站?  如何快速搭建安全的FTP站点?  西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?  如何为不同团队 ID 动态生成多个“认领值班”按钮  如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?  googleplay官方入口在哪里_Google Play官方商店快速入口指南  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】  微信h5制作网站有哪些,免费微信H5页面制作工具?  Laravel如何生成URL和重定向?(路由助手函数)  Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制  如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?  Android仿QQ列表左滑删除操作  车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?  Win11搜索栏无法输入_解决Win11开始菜单搜索没反应问题【技巧】  C++时间戳转换成日期时间的步骤和示例代码  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  Windows驱动无法加载错误解决方法_驱动签名验证失败处理步骤  Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】  魔方云NAT建站如何实现端口转发?  深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?  Laravel distinct去重查询_Laravel Eloquent去重方法  如何快速启动建站代理加盟业务?  laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析  详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点  如何快速查询网站的真实建站时间?  Laravel如何使用Socialite实现第三方登录?(微信/GitHub示例)  Laravel怎么实现API接口鉴权_Laravel Sanctum令牌生成与请求验证【教程】  Laravel怎么使用Markdown渲染文档_Laravel将Markdown内容转HTML页面展示【实战】  如何用5美元大硬盘VPS安全高效搭建个人网站?