c++如何实现超大文件读取

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++如何实现超大文件读取相关的知识,希望对你有一定的参考价值。

不要把整个文件读到内存(内存不够),最好能从硬盘定位然后从该位置开始读一段到内存里或者直接读出来

1、 创建文件(CreateFile),如下:
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
参数解析:

lpFileName:需要创建或者打开的文件名字
dwDesiredAccess:文件的打开方式,GENERIC_READ(只读), GENERIC_WRITE(只写), GENERIC_READ | GENERIC_WRITE(读写)
dwShareMode:文件的共享方式,如:FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE
lpSecurityAttributes:文件的安全属性,通常为空
dwCreationDisposition:文件的访问方式,如:CREATE_ALWAYS, CREATE_NEW, OPEN_ALWAYS, OPEN_EXISTING, or TRUNCATE_EXISTING(只能是其中一种,不能组合使用)
dwFlagsAndAttributes:文件属性和标志
hTemplateFile:模板文件句柄

2、 创建文件内存映射(CreateFileMapping),如下:
HANDLE WINAPI CreateFileMapping(
_In_ HANDLE hFile,
_In_opt_ LPSECURITY_ATTRIBUTES lpAttributes,
_In_ DWORD flProtect,
_In_ DWORD dwMaximumSizeHigh,
_In_ DWORD dwMaximumSizeLow,
_In_opt_ LPCTSTR lpName
);
参数解析:

hFile:需要创建文件内存映射的文件句柄
lpAttributes:安全属性指针
flProtect:文件内存映射访问模式
dwMaximumSizeHigh:内存映射大小的高32位
dwMaximumSizeLow:内存映射大小的低32位
lpName:内存映射的名字

3、 获得系统分配粒度(GetSystemInfo),如下:
void WINAPI GetSystemInfo(
_Out_ LPSYSTEM_INFO lpSystemInfo
)
参数解析:

lpSystemInfo:SYSTEM_INFO结构指针

4、 文件内存映射视图(MapViewOfFile)
LPVOID WINAPI MapViewOfFile(
_In_ HANDLE hFileMappingObject,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwFileOffsetHigh,
_In_ DWORD dwFileOffsetLow,
_In_ SIZE_T dwNumberOfBytesToMap
);
参数解析:

hFileMappingObject:文件内存映射句柄
dwDesiredAccess:访问方式,如FILE_MAP_ALL_ACCESS、FILE_MAP_COPY等
dwFileOffsetHigh:文件偏移大小高位
dwFileOffsetLow:文件偏移大小低位
注意:文件偏移必须是系统粒度的整数倍
dwNumberOfBytesToMap:映射多少数据进视图

5、 撤销文件内存映射视图(UnmapViewOfFile)
BOOL WINAPI UnmapViewOfFile(
_In_ LPCVOID lpBaseAddress
);
参数解析:

lpBaseAddress:文件视图指针

三、使用方法
1、 使用CreateFile创建/打开一文件,这个文件对象标识了磁盘上将要用作内存映射文件的文件。
2、 使用CreateFileMapping函数来创建一个文件映射内核对象,告诉系统文件的尺寸以及访问文件的方式。
3、 使用MapViewOfFile函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。
4、 通过UnmapViewOfFile完成从进程的地址空间撤消文件数据的映像、通过CloseHandle关闭前面创建的文件映射对象和文件对象。
四、使用举例
功能:在一个大文件(文件名BigFile.txt)中,查找一字符串”End”。实现根据字符串"End"将文件截断的功能。
代码工程名IOTest.sln:
详细代码见BigFileFun.h、BigFileFun.cpp
调用代码见IOTest.cpp中_tmain函数
参考技术A fopen
fseek
只要文件不超过4G 用这两个函数都可以做到
#include <stdio.h>
int main()

FILE *fp = fopen("test.txt", "r");

fseek(fp, 2*1024*1024, SEEK_SET);


这个是seek到2M的参考代码追问

不好意思我文件10G,比如我现在想读5G位置的10个字节怎么读

追答

windows系统吗? 64位还是32位的?

什么操作系统
用的是什么编译器?
查一下有没有这个函数支持
_lseeki64
如果没有的话 那么只能用feek一点点累加了
int i;
for(i = 0; i < 5; i ++)
fseek(fp, 1024*1024*1024, SEEK_CUR);//一次1G
fread(buffer, 10, 1, fp);
如果有
_lseeki64 那么直接调用就好

追问

我尝试过读那个文件,过一段用tellg()输出,结果到2亿多(好像是int的上限)就突然变成负的2亿多了,感觉他的返回值只有int大小,但我要把后面的位置存下来,以后直接从那个位置用seekg读。怎么办?麻烦你了,给你加分

追答

还是取决于你的环境
如果你的编译环境本身不支持大文件支持 那么只能用fseek一点一点累加过去
但是想通过tellg操作 就根本不能实现了
除非你的环境支持大文件操作
不然要么你换一个环境 要么添加一个大文件读写的patch

PS:你用的是什么编译器呢?

或者这样你看可以接受不?
在读写文件的时候 你自己再保存一个位置值
这样每次对文件操作的时候 都通过你自己保存的位置来操作
换句话说 把你所有的操作做一个封装
底层只调用系统的fopen fseek fread fwrite 其他的都不调用

本回答被提问者采纳
参考技术B 对于超大文件的读取只能够循环读取文件;边读取边显示。
比如视频都是边播放边读取,所以对于有些高清视频拖动时就会比较卡。
参考技术C 用CreateFile函数 参考技术D 内存映射。

以上是关于c++如何实现超大文件读取的主要内容,如果未能解决你的问题,请参考以下文章

Java难点攻克「大文件处理系列」让我们一起去挑战一下如何读取一个较大或者超大的数据文件

PHP快速按行读取CSV大文件的封装类分享(也适用于其它超大文本文件)

如何用PHPExcel读取超大excel文件

#私藏项目实操分享#Java深层系列「技术盲区」让我们一起去挑战一下如何读取一个较大或者超大的文件数据!

使用生成器读取一个超大文件

java读取大文件 超大文件的几种方法