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;
Delphi 中内存映射对于大文件的使用的更多相关文章
- Java中用内存映射处理大文件
在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如 ...
- Java使用内存映射实现大文件的上传
在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如 ...
- 【转】Linux 中清空或删除大文件内容的五种方法(truncate 命令清空文件)
原文: http://www.jb51.net/article/100462.htm truncate -s 0 access.log -------------------------------- ...
- NET 4 中 内存映射文件
原文链接 : http://blogs.msdn.com/salvapatuel/archive/2009/06/08/working-with-memory-mapped-files-in-net- ...
- delphi中如何将一整个文件读入内存
来源 https://bbs.csdn.net/topics/390985048 分配一块大内存吧,要是一下申请不了64M那么大,就多申请几块小的,用个链表连起来.用FileStream类的方法读取文 ...
- python 中内存映射二进制文件
内存映射一个文件并不会导致整个文件被读取到内存中. 也就是说,文件并没有被复制到内存缓存或数组中.相反,操作系统仅仅为文件内容保留了一段虚拟内存. 当你访问文件的不同区域时,这些区域的内容才根据需要被 ...
- 在ASP.NET中支持断点续传下载大文件(ZT)
IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交 ...
- 在React中使用WebUploader实现大文件分片上传的踩坑日记!
前段时间公司项目有个大文件分片上传的需求,项目是用React写的,大文件分片上传这个功能使用了WebUploader这个组件. 具体交互是: 1. 点击上传文件button后出现弹窗,弹窗内有选择文件 ...
- FileOutputStream字节输出流和FileInputStream输入流(切记:out是输出到本地中,in是输入到程序中)这里介绍大文件和小文件的读取方式
//FileOutputStream public class FileOutputStreamDemo { /**字节流:适用于任何文件,以字节为单位,进行读写操作 *字节流操作步骤: *1.创 ...
随机推荐
- react-redux升级到7.0.x后报错"TypeError: Object(...) is not a function"
同样一套代码,react-redux 6.0.x 的版本下可以正常运行,在7.0.x的版本下出现报错,而且报错原因实在太难定位了 package.json { ... "react-redu ...
- 洛谷 - P2257 - YY的GCD - 莫比乌斯反演 - 整除分块
https://www.luogu.org/problemnew/show/P2257 求 \(n,m\) 中 \(gcd(i,j)==p\) 的数对的个数 求 $\sum\limits_p \sum ...
- ZOJ3158 【DFS】
题意: 从上往下切割,不能切边缘,然后问你两个差值,要小于给出的值. 思路: 直接爆搜,枚举每一行的劈开位置: #include<bits/stdc++.h> using namespac ...
- laravel定义全局变量并输出(实例)
在config目录创建一个 bl.php ,内容如下 <?php return [ 'IND' => "1321232", 'das' => "奥术大师 ...
- 3d工具收集
Poser 是Metacreations公司推出的一款三维动物.人体造型和三维人体动画制作的极品软件.用过Poser 2与Poser 3的朋友一定能感受到Poser的人体设计和动画制作是那么的轻松自如 ...
- 为GitHub项目添加协议
解决方法 如果一开始在GitHub上创建仓库时没有添加协议,可以用以下方式来重新添加相关的协议: 打开GitHub上的某个仓库,点击Create new file: 在新建文件的页面上,输入文件名LI ...
- #13:人十我一Orz——6
水题放送,写得依旧丑: #include <cstdio> #include <cstring> #include <cmath> #include <alg ...
- bat脚本启动Burp
我的burp点击之后并不会直接打开,需要用命令启动,所以在网上找了一下快捷启动的方法. ①新建一个文本文档,输入start javaw -jar “burp路径”, ②另存为***.bat,文件类型选 ...
- DB2 函数
1.大小写转换 转大写UPPER 转小写LOWER
- “玲珑杯”ACM比赛 Round #4 E -- array DP
http://www.ifrog.cc/acm/problem/1050?contest=1006&no=4 DP[val]表示以val这个值结尾的等差数列有多少个 DP[val] += DP ...