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

Posted fengbingchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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。

GitHubhttps://github.com/fengbingchun/Messy_Test

以上是关于Windows/Linux上使用fopen相关函数读取大文件的主要内容,如果未能解决你的问题,请参考以下文章

用fopen打开的文件

fopen参数介绍

fopen参数介绍

C语言文件的相关操作

C语言中的fopen函数的具体实现是怎么样?

阻塞调用读取管道