ITPub博客

首页 > 大数据 > 数据分析 > VC 快速读取大文件的方法

VC 快速读取大文件的方法

数据分析 作者:zzt5661693 时间:2014-03-07 22:06:59 0 删除 编辑

1、 用内存映射文件技术,对大文件效果很明显,打开,创建速度很快
   建立内存映射文件的步骤如下
   (1)使用CreateFile函数创建文件。
   (2)使用CreateFileMapping创建文件映射内核对象。
   (3)使用MapViewOfFile将文件数据映射到进程的数据指针。
   (4)将头指针(m_FileHead)指向数据指针的文件头结构。

(5)UnmapViewOfFile

  Windows系统中,Win32 APIMFC均提供有支持文件处理的函数和类。一般来说,这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的。
  内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。
  首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。
  CreateFileMapping()在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间,实际上相当于加载文件中指定的数据到内存中。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。
  实际上操作文件映射对象就相当于操作VC++文件读写方式下的文件内部指针。
  而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有232 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:
  1)映射从文件开头的映像;
  2)对该映像进行访问;
  3)取消此映像;
  4)映射一个从文件中的一个更深的位移开始的新映像;
  5)重复步骤2,直到访问完全部的文件数据。

示例代码:
  在本例中,首先通过GetFileSize()得到被处理文件长度(64位)的高32位和低32位值。然后在映射过程中设定每次映射的块大小为1000倍的分配粒度(系统的数据分块大小),如果文件长度小于1000倍的分配粒度时则将块大小设置为文件的实际长度。在处理过程中由映射、访问、撤消映射构成了一个循环处理。其中,每处理完一个文件块后都通过关闭文件映射对象来对每个文件块进行整理。CreateFileMapping()MapViewOfFile()等函数是专门用来进行内存文件映射处理用的。

// 选择文件

CFileDialog fileDlg(TRUE, "*.txt", "*.txt", NULL, "文本文件 (*.txt)|*.txt||", this);

fileDlg.m_ofn.Flags |= OFN_FILEMUSTEXIST;

fileDlg.m_ofn.lpstrTitle "通过内存映射文件读取数据";

if (fileDlg.DoModal() == IDOK)

{

// 创建文件对象

HANDLE hFile CreateFile(fileDlg.GetPathName(), GENERIC_READ GENERIC_WRITE,

0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE)

{

TRACE("创建文件对象失败,错误代码:%drn", GetLastError());

return;

}

// 创建文件映射对象

HANDLE hFileMap CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);

if (hFileMap == NULL)

{

TRACE("创建文件映射对象失败,错误代码:%drn", GetLastError());

return;

}

// 得到系统分配粒度

SYSTEM_INFO SysInfo;

GetSystemInfo(&SysInfo);

DWORD dwGran SysInfo.dwAllocationGranularity;

// 得到文件尺寸

DWORD dwFileSizeHigh;

__int64 qwFileSize GetFileSize(hFile, &dwFileSizeHigh);

qwFileSize |= (((__int64)dwFileSizeHigh) << 32);

// 关闭文件对象

CloseHandle(hFile);

// 偏移地址 

__int64 qwFileOffset 0;

// 块大小

DWORD dwBlockBytes 1000 dwGran;

if (qwFileSize 1000 dwGran)

dwBlockBytes (DWORD)qwFileSize;

while (qwFileOffset 0)

{

// 映射视图

LPBYTE lpbMapAddress (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, 

(DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset 0xFFFFFFFF),

dwBlockBytes);

if (lpbMapAddress == NULL)

{

TRACE("映射文件映射失败,错误代码:%drn", GetLastError());

return;

}

// 对映射的视图进行访问

for(DWORD 0; dwBlockBytes; i++)

BYTE temp *(lpbMapAddress i);

// 撤消文件映像

UnmapViewOfFile(lpbMapAddress);

// 修正参数

qwFileOffset += dwBlockBytes;

qwFileSize -= dwBlockBytes;

}

// 关闭文件映射对象句柄

CloseHandle(hFileMap);

AfxMessageBox("成功完成对文件的访问");

}
   在多线程里,文件只用打开一次,不会频繁的seek

说明:内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,这样取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,能够实现实现数据的实时备份。

 

2、 用多线程多块复制技术
   主线程读出文件A大小S,创建同样大小的一个空文件B后关闭文件B
   本技术原理是把文件分成N块来复制,即根据需要产生N个线程,每个线程只负责复制第S/N大小的内容复制。
    需要再分别以共享读方式打开A,共享写方式打开B,分别Seek到自己负责的那一段文件的开始,复制到B文件对应部分,直接自己负责的那一段结束时线程退出。
    当全部线程退出,文件复制完成。

   多线程只会使速度更慢,在单线程里用ReadWrite是最快的。这就是因为硬盘串行工作机制的限制,多文件并行操作时,时间都花在磁头摆动上了。并且在缓存读取上,命中率也将大大降低。所以我们要避免使用windows缓存机制,并尽量不要同时读写多段文件,尽量读写连续的文件块。

<!-- 正文结束 -->

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/7932887/viewspace-1120898/,如需转载,请注明出处,否则将追究法律责任。

上一篇: 没有了~
下一篇: 没有了~
请登录后发表评论 登录
全部评论