admin管理员组

文章数量:1794759

Windows/Linux上使用fopen相关函数读取大文件

Windows/Linux上使用fopen相关函数读取大文件

在介绍读取大文件之前,先了解下<cstdint>文件,标准头文件,存放固定宽度整数类型,如int32_t, uint32_t,不管在32位上还是64位上,长度都为4个字节;int64_t, uint64_t,不管在32位上还是64位上,长度都为4个字节。对于int,无论在32位上还是在64位上,长度都为4个字节。

对于long, long long, size_t类型,在Windows和Linux上会有所不同。以下是汇总:

使用fopen读取大文件相关函数声明如下:注意它们的参数类型和返回类型

FILE* fopen(const char* filename, const char* mode); int fseek(FILE* stream, long int offset, int origin); long int ftell(FILE* stream); size_t fread(void* ptr, size_t size, size_t count, FILE* stream); size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream); int fclose ( FILE * stream ); // only windows, __int64 == long long int _fseeki64(FILE *stream, __int64 offset, int origin); __int64 _ftelli64(FILE *stream);

现代的应用程序都运行在一个内存空间里,在32位的系统里,这个内存空间拥有4GB(2的32次方)的寻址能力。应用程序可以直接使用32位的地址进行寻址,这被称为平坦(flat)的内存模型。在平坦的内存模型中,整个内存是一个统一的地址空间,用户可以使用一个32位的指针访问任意内存位置。大多数操作系统都会将4GB的内存空间中的一部分挪给内核使用,应用程序无法直接访问这一段内存,这一部分内存地址被称为内核空间。Windows在默认情况下会将高地址的2GB空间分配给内核(也可配置为1GB),而Linux默认情况下将高地址的1GB空间分配给内核。用户使用的剩下2GB或3GB的内存空间称为用户空间。因此在32位系统里,一次性加载大于2G或3G的文件,使用普通的方法是行不通的。在64位系统里则可以。

在windows上,要使用_fseeki64和_ftelli64函数替代fseek和ftell函数,否则得到的值是无效的,因为fseek和ftell的参数类型或返回类型为long,在windows上,无论是32位还是64位,long的长度都为4个字节,超出了所能接受的最大值范围。执行结果如下图所示:以vs2013.5_pro_enu.iso为例,第1个窗口显示的是此文件的真实值大小;第2窗口为32位上的执行结果,第3个窗口为64位上执行结果,可见使用_fseeki64和_ftelli64后,均可获取到真实值大小。

在linux上,当文件大于2G时,在32位上,调用fopen函数会直接返回空。执行结果如下图所示:以Ubuntu_14_04_3.rar为例,第1个窗口显示的是此文件的真实值大小;第2窗口为64位上的执行结果,可见与真实值大小一致;第3个窗口为32位上执行结果,大于2G文件,在32位上不能正常调用fopen函数。

测试代码如下所示:

int test_load_big_file() { fprintf(stdout, "int32_t: %d, uint32_t: %d\\n", sizeof(int32_t), sizeof(uint32_t)); fprintf(stdout, "int64_t: %d, uint64_t: %d\\n", sizeof(int64_t), sizeof(uint64_t)); fprintf(stdout, "int: %d\\n", sizeof(int)); fprintf(stdout, "long: %d, long long: %d, size_t: %d\\n", sizeof(long), sizeof(long long), sizeof(size_t)); #ifdef _MSC_VER const char* name = "E:/GitCode/Messy_Test/testdata/test.tar"; #else const char* name = "testdata/test.tar"; #endif FILE* file = fopen(name, "rb"); if (!file) { fprintf(stderr, "fail to open file: %s\\n", name); return -1; } #ifdef _MSC_VER auto ret = _fseeki64(file, 0, SEEK_END); if (ret != 0) { fprintf(stderr, "fail to _fseeki64: %d\\n", ret); return -1; } auto length = _ftelli64(file); fprintf(stdout, "file length: %lld\\n", length); #else auto ret = fseek(file, 0, SEEK_END); if (ret != 0) { fprintf(stderr, "fail to _fseeki64: %d\\n", ret); return -1; } auto length = ftell(file); fprintf(stdout, "file length: %lld\\n", length); #endif fclose(file); return 0; }

如果对大文件可分块处理,也可通过反复调用fread函数对大文件进行操作。

除了使用fopen还可以使用std::ifstream。

GitHub:github/fengbingchun/Messy_Test

本文标签: 函数大文件windowsLinuxfopen