VC++ 大文件读写(大于4G)

关于内存映射文件技术处理大文件的读写 Windows对文件的读写提供了很丰富的操作手段,如:1. FILE *fp, fstearm...; (C/C++)2. CFile, CStdioFile...; (MFC)3. CreateFile, ReadFile...;(API)...在处理一般的文件(文本/非文本),这些足够了。然而在处理比较大的文件如几十M, 几百M, 甚至上G的文件, 这时再用一般手段处理,系统就显的力不从心 阅读详情

VC++中使用内存映射文件处理大文件 

原文链接:http://blog.youkuaiyun.com/wesleyluo/article/details/5624278

  摘要: 本文给出了一种方便实用的解决大文件的读取、存储等处理的方法,并结合相关程序代码对具体的实现过程进行了介绍。 

  引言 

  文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows核心编程技术展开讨论。 

  内存映射文件 

  内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。 

  内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。下面给出使用内存映射文件的一般方法: 

  首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。 

  内存映射文件相关函数 

  在使用内存映射文件时,所使用的API函数主要就是前面提到过的那几个函数,下面分别对其进行介绍: 

HANDLE CreateFile(LPCTSTR lpFileName, 
DWORD dwDesiredAccess, 
DWORD dwShareMode, 
LPSECURITY_ATTRIBUTES lpSecurityAttributes, 
DWORD dwCreationDisposition, 
DWORD dwFlagsAndAttributes, 
HANDLE hTemplateFile); 

  函数CreateFile()即使是在普通的文件操作时也经常用来创建、打开文件,在处理内存映射文件时,该函数来创建/打开一个文件内核对象,并将其句柄返回,在调用该函数时需要根据是否需要数据读写和文件的共享方式来设置参数dwDesiredAccess和dwShareMode,错误的参数设置将会导致相应操作时的失败。 

HANDLE CreateFileMapping(HANDLE hFile, 
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, 
DWORD flProtect, 
DWORD dwMaximumSizeHigh, 
DWORD dwMaximumSizeLow, 
LPCTSTR lpName); 

  CreateFileMapping()函数创建一个文件映射内核对象,通过参数hFile指定待映射到进程地址空间的文件句柄(该句柄由CreateFile()函数的返回值获取)。由于内存映射文件的物理存储器实际是存储于磁盘上的一个文件,而不是从系统的页文件中分配的内存,所以系统不会主动为其保留地址空间区域,也不会自动将文件的存储空间映射到该区域,为了让系统能够确定对页面采取何种保护属性,需要通过参数flProtect来设定,保护属性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分别表示文件映射对象被映射后,可以读取、读写文件数据。在使用PAGE_READONLY时,必须确保CreateFile()采用的是GENERIC_READ参数;PAGE_READWRITE则要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE参数;至于属性PAGE_WRITECOPY则只需要确保CreateFile()采用了GENERIC_READ和GENERIC_WRITE其中之一即可。DWORD型的参数dwMaximumSizeHigh和dwMaximumSizeLow也是相当重要的,指定了文件的最大字节数,由于这两个参数共64位,因此所支持的最大文件长度为16EB,几乎可以满足任何大数据量文件处理场合的要求。 

LPVOID MapViewOfFile(HANDLE hFileMappingObject, 
DWORD dwDesiredAccess, 
DWORD dwFileOffsetHigh, 
DWORD dwFileOffsetLow, 
DWORD dwNumberOfBytesToMap); 

  MapViewOfFile()函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject为CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度: 

SYSTEM_INFO sinf; 
GetSystemInfo(&sinf); 
DWORD dwAllocationGranularity = sinf.dwAllocationGranularity; 

  参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于Windows 9x操作系统,如果MapViewOfFile()无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。 

  在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下: 

BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); 

  唯一的参数lpBaseAddress指定了返回区域的基地址,必须将其设定为MapViewOfFile()的返回值。在使用了函数MapViewOfFile()之后,必须要有对应的UnmapViewOfFile()调用,否则在进程终止之前,保留的区域将无法释放。除此之外,前面还曾由CreateFile()和CreateFileMapping()函数创建过文件内核对象和文件映射内核对象,在进程终止之前有必要通过CloseHandle()将其释放,否则将会出现资源泄漏的问题。 

  除了前面这些必须的API函数之外,在使用内存映射文件时还要根据情况来选用其他一些辅助函数。例如,在使用内存映射文件时,为了提高速度,系统将文件的数据页面进行高速缓存,而且在处理文件映射视图时不立即更新文件的磁盘映像。为解决这个问题可以考虑使用FlushViewOfFile()函数,该函数强制系统将修改过的数据部分或全部重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘。

使用内存映射文件处理大文件应用示例 

  下面结合一个具体的实例来进一步讲述内存映射文件的使用方法。该实例从端口接收数据,并实时将其存放于磁盘,由于数据量大(几十GB),在此选用内存映射文件进行处理。下面给出的是位于工作线程MainProc中的部分主要代码,该线程自程序运行时启动,当端口有数据到达时将会发出事件hEvent[0],WaitForMultipleObjects()函数等待到该事件发生后将接收到的数据保存到磁盘,如果终止接收将发出事件hEvent[1],事件处理过程将负责完成资源的释放和文件的关闭等工作。下面给出此线程处理函数的具体实现过程: 

…… 
// 创建文件内核对象,其句柄保存于hFile 
HANDLE hFile = CreateFile("Recv1.zip", 
GENERIC_WRITE | GENERIC_READ, 
FILE_SHARE_READ, 
NULL, 
CREATE_ALWAYS, 
FILE_FLAG_SEQUENTIAL_SCAN, 
NULL); 

// 创建文件映射内核对象,句柄保存于hFileMapping 
HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE, 
0, 0x4000000, NULL); 
// 释放文件内核对象 
CloseHandle(hFile); 

// 设定大小、偏移量等参数 
__int64 qwFileSize = 0x4000000; 
__int64 qwFileOffset = 0; 
__int64 T = 600 * sinf.dwAllocationGranularity; 
DWORD dwBytesInBlock = 1000 * sinf.dwAllocationGranularity; 

// 将文件数据映射到进程的地址空间 
PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping, 
FILE_MAP_ALL_ACCESS, 
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock); 
while(bLoop) 
{ 
// 捕获事件hEvent[0]和事件hEvent[1] 
DWORD ret = WaitForMultipleObjects(2, hEvent, FALSE, INFINITE); 
ret -= WAIT_OBJECT_0; 
switch (ret) 
{ 
// 接收数据事件触发 
case 0: 
// 从端口接收数据并保存到内存映射文件 
nReadLen=syio_Read(port[1], pbFile + qwFileOffset, QueueLen); 
qwFileOffset += nReadLen; 

// 当数据写满60%时,为防数据溢出,需要在其后开辟一新的映射视图 
if (qwFileOffset > T) 
{ 
T = qwFileOffset + 600 * sinf.dwAllocationGranularity; 
UnmapViewOfFile(pbFile); 
pbFile = (PBYTE)MapViewOfFile(hFileMapping, 
FILE_MAP_ALL_ACCESS, 
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock); 
} 
break; 

// 终止事件触发 
case 1: 
bLoop = FALSE; 

// 从进程的地址空间撤消文件数据映像 
UnmapViewOfFile(pbFile); 

// 关闭文件映射对象 
CloseHandle(hFileMapping); 
break; 
} 
} 
… 

  在终止事件触发处理过程中如果只简单的执行UnmapViewOfFile()和CloseHandle()函数将无法正确标识文件的实际大小,即如果开辟的内存映射文件为30GB,而接收的数据只有14GB,那么上述程序执行完后,保存的文件长度仍是30GB。也就是说,在处理完成后还要再次通过内存映射文件的形式将文件恢复到实际大小,下面是实现此要求的主要代码: 

// 创建另外一个文件内核对象 
hFile2 = CreateFile("Recv.zip", 
GENERIC_WRITE | GENERIC_READ, 
FILE_SHARE_READ, 
NULL, 
CREATE_ALWAYS, 
FILE_FLAG_SEQUENTIAL_SCAN, 
NULL); 

// 以实际数据长度创建另外一个文件映射内核对象 
hFileMapping2 = CreateFileMapping(hFile2, 
NULL, 
PAGE_READWRITE, 
0, 
(DWORD)(qwFileOffset&0xFFFFFFFF), 
NULL); 

// 关闭文件内核对象 
CloseHandle(hFile2); 

// 将文件数据映射到进程的地址空间 
pbFile2 = (PBYTE)MapViewOfFile(hFileMapping2, 
FILE_MAP_ALL_ACCESS, 
0, 0, qwFileOffset); 

// 将数据从原来的内存映射文件复制到此内存映射文件 
memcpy(pbFile2, pbFile, qwFileOffset); 

file://从进程的地址空间撤消文件数据映像 
UnmapViewOfFile(pbFile); 
UnmapViewOfFile(pbFile2); 

// 关闭文件映射对象 
CloseHandle(hFileMapping); 
CloseHandle(hFileMapping2); 

// 删除临时文件 
DeleteFile("Recv1.zip"); 

  结论 

  经实际测试,内存映射文件在处理大数据量文件时表现出了良好的性能,比通常使用CFile类和ReadFile()和WriteFile()等函数的文件处理方式具有明显的优势。本文所述代码在Windows 98下由Microsoft Visual C++ 6.0编译通过。


原文链接:http://blog.youkuaiyun.com/wesleyluo/article/details/5624278

c/c++ qt如何快速的解析(300MB-1G)大文件 读取千万行文本文件,使用QTextEdit分页显示。 阅读详情

相关推荐

VC++实现Turbo码

Turbo 编码器是一种由两个递归系统卷积码(RSC)编码器通过随机交织器并行连接构成的通信编码装置。

bcbobo21cn的专栏 1639

VC++中使用内存映射文件处理大文件

 引言  文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是

DellXu的专栏 1353

VC++环境下MPEG-4视频解码技术详解与实现

本文还有配套的精品资源,点击获取 简介:MPEG-4作为一项高效的数字视频编码标准,由ISO和IEC共同制定,支持多种媒体集成,广泛应用于多媒体通信和数字电视等领域。在VC++中实现MPEG-4解码技术需要掌握C++编程和多媒体库的使用。本文将介绍MPEG-4解码技术的核心,包括宏块处理、DCT变换、量化、熵编码解码、反量化与IDCT、运动补偿以及图像重建。文章还详细阐述了...

weixin_31938351的博客 1156

适用在4G硬盘Eee PC上的编程工具:VC++与MSDN绿色版

问题的提出:1.小巧的4G硬盘容量、512M内存的Eee PC一部 外插8G的SD卡2.需要重新学习C++程序设计3.需要有C++程序设计的开发平台问题的解决:1.VC++与MSDN绿色版2.地址: VC++与MSDN绿色版分卷01:http://download.youkuaiyun.com/source/1797071 VC++与MSDN绿色版分卷02:http://download.csdn.n...

weixin_30507269的博客 92

内存映射I/O大文件性能测试代码

内存映射I/O的读写性能测试,以内存映射I/O和和系统调用write写500M文件为例

VC++读写大型二进制文件

VC++读写大型二进制文件,对话框程序,vc++6.0开发

用VC++实现上网拨号功能

  用VC++实现上网拨号功能 大连捷通电脑技术有限公司 陈珏 ---- 现在的时代是网络的时代,网络应用程序的应用越来越广泛,在自己设计的应用程序中实现直接拨号上网,给应用程序的使用者带了很多方便,本应用程序就是在中文windows95操作系统上,用VC++5.0开发的实现拨号上网的一个小程序,希望能对大家有所帮助。下面是实现这一功能的源程序: ---- 首先创建

1233

c++实现向web服务器上传文件

vs2013 c++实现上传的客户端,服务端为java写的web工程,模拟post方法,可以上传大文件

VC++ Hook截取鼠标点击窗口消息的问题!全局钩子

VC++ Hook截取鼠标点击窗口消息的问题!全局钩子,高手进!悬赏分:0 - 提问时间: 2010年05月19日 06时20分我在做毕业设计,现在遇到一个问题,像求教高手:我想实现如下功能,当鼠标点击一个按钮的时候,实现一个功能,然后把这个鼠标的消息在传递下去,继续传给原来的应用程序,需要说明的是按钮的句柄已知,设为hwnd,并且这个按钮不是本应用程序的按钮,访问其他的进程需要全局钩子,所以编译出的应该是dll文件;如何实现这个功能,求教高手,我这两天有点晕,呵呵,代码或工程请发到ipconfig@163

taiq的专栏 1万+

可读4G以上WINOWS大文件VC6原码

可读4G以上WINOWS大文件VC6原码,用的是API定义的功能函数,已测试8G以上文件!参阅了部分网上资料。

file_map.zip_C++map_C++文件映射_MAP文件_内存_内存映射文件

内存映射文件的使用

vc++ 6.0 堆栈

vc++6.0 程序栈空间默认1M,每个活动记录默认80byte

bjdu_jlxs的专栏 1664

西门子OMAC PackML V2022在S7-1200/S7-1500中的状态机设计与应用实践

本文详细解析了西门子OMAC PackML V2022在S7-1200/S7-1500中的状态机设计与应用实践。通过实际案例和代码示例,介绍了状态机的基础认知、硬件配置、逻辑开发、调试技巧以及与MES系统集成的高级应用,帮助工程师高效实现设备自动化控制。

weixin_29255731的博客 529

Windows环境下Python中wordcloud的使用——自己踩过的坑 2017.08.08

Python-wordcloud-初次尝试 作为一个Python初学者,作为学习的开始,这次我根据《一件有趣的事:用Python 爬了爬自己的微信朋友》中的方法测试了一遍效果。这里记录下目前用的wordcloud这个包时遇到的一些问题吧。(具体执行的完整代码我就不放进来了,反正也是依据链接中写的为主。) 使用wordcloud可以方便地生成云词图,这在数据可视化的方面会给我们带来

heyuexianzi的博客 4万+

C# 死锁:原理、诊断与解决方案深度解析

本文深入探讨了C#中的死锁问题,详细介绍了死锁的基本概念、形成机制、常见场景、诊断方法及解决方案。死锁通常发生在多线程环境下,当多个线程因争夺资源而互相等待时,导致程序无法继续执行。文章通过代码示例展示了常见的死锁场景,如锁的嵌套、同步上下文死锁和线程池死锁,并提供了使用Visual Studio、WinDbg等工具进行死锁诊断的方法。此外,文章还提出了多种预防和解决死锁的策略,包括保持锁顺序一致性、使用锁超时机制、避免同步阻塞异步代码以及使用更高级的同步原语。通过这些方法,开发者可以有效减少死锁的发生,提

记录学习的过程 1407

计量经济学(因果关系探究-空间双重差分模型: 破解空间溢出效应的因果分析工具)

本文介绍了传统双重差分(DID)模型及其局限性,重点阐述了空间DID模型的必要性。传统DID通过组间差分分离干预效应,但无法处理空间溢出效应。空间DID模型引入空间计量方法,包括三种类型:空间自回归模型(SDID-SAR)捕捉因变量溢出,空间误差模型(SDID-SEM)处理残差相关性,空间杜宾模型(SDID-SDM)同时考虑因变量和自变量的空间滞后。模型选择需结合空间相关性检验,适用于环境政策、房地产限购等存在空间交互效应的研究场景。

YBAdvanceFu的博客 2030

程序设计:从VC++6.0到Visual Studio 2008

<br />   在学习C或是C++的时候好多都是在VC++环境下编译运行的,那么随着对软件开发的要求不断提高,基本上已经不再此环境下开发,绝大部分已经在.net 环境开发了,也就是我们常用的软件开发环境VS(visual studio),因此环境变迁多多少少有点不一样。<br />   就从最基础的一点开始。VS2008(以VS2008为例)大概是3G到4G左右,一般我们使用企业级或者是开发版本(还有一些速成版之类的),且最好是 标准版本(微软的原版本)。首先以建立空程序为例:点击文件--》新

独风孤月http://www.bobolee.me 822

基于Simulink的魔术公式轮胎动力学仿真

本篇博文主要用来记录汽车系统动力学课程的学习,仅供课程阶段参考,与实际工程应用还有较大距离。基于Simulink的魔术公式轮胎动力学仿真本实例主要有以下任务。1.建立魔术公式轮胎动力学仿真模型。2.绘制不同载荷下的轮胎纵向力—滑动率关系曲线。3.绘制不同载荷下的轮胎侧向力—侧偏角关系曲线。4.绘制不同载荷下的轮胎回正力矩—侧偏角关系曲线。提示:以下是本篇文章正文内容,下面案例可供参考。

shy_201992的博客 6940

Twisted-18.7.0-cp37-cp37m-win_amd64.whl下载,以及安装Scrapy时,报错缺VC++ 14.0解决措施

如题,先附上某盘地址链接:https://pan.baidu.com/s/1OqMTCAtJ1wQVCN87kT6ETA 密码:31qe 安装时出现,缺少VC++ 14.0 说明:出现这种问题,不需要去安装VC++14.0 ,只需要去python库上下载相应的包安装即可。(当然也可以按照提示,下载4G的应用,但我下载后出现另外错误。。。。) 1.在python库中下载twisted相应的...

weixin_42117682的博客 1万+

【Ultrascale+ MPSOC学习记录】6、从eMMC启动翻车

因为MPSOC还有一种启动模式是eMMC,接下来咱就实现从eMMC启动。

ryo891012的博客 474

ppt保存时嵌入字体,但找不到字体使用的位置的解决办法

摘要 当PPT提示无法保存嵌入字体时,可通过VBA代码快速定位使用该字体的文本。具体步骤如下: 按ALT+F11打开VBA编辑器,新建模块并粘贴提供的代码; 运行代码后输入目标字体名称(如“方正兰亭黑简体”); 程序将列出所有使用该字体的幻灯片编号及对应文本内容; 修改这些文本的字体即可解决保存问题。该方法高效精准,适用于排查PPT字体嵌入失败的情况。(150字)

智慧空间实验室 1397

【C++对象模型】使用gcc、clang和VC++显示C++类的内存布局

引言 各种C++实现对C++类/对象的内存布局可能有所不同,包括数据成员的顺序、虚函数表(virtual table: vtbl)的结构、继承关系的处理等。了解C++类/对象的布局,对于理解C++各种机制,正确合理地进行设计和开发有很大的帮助。 主流编译器的支持 本文所述的3款主流编译器都提供打印/导出C++类/对象的内存布局的功能,现用表格列出其特性和用法,测试用的代码文件列于文...

dengdi8115的博客 933

什么是 AI Agent?它和直接调用大模型 API 有什么区别?

什么是 AI Agent?它和直接调用大模型 API 有什么区别?

我哎GIS博客 6477
上一篇: 卡特兰数
下一篇: TCP的拥塞控制机制
静水若流深
博客等级 码龄14年 5粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值