在介绍读取大文件之前,先了解下<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。