Windows/Linux上使用fopen相关函数读取大文件 我就是我 2022-10-18 11:22 596阅读 0赞 在介绍读取大文件之前,先了解下<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上会有所不同。以下是汇总: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdiaW5nY2h1bg_size_16_color_FFFFFF_t_70][] 使用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后,均可获取到真实值大小。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdiaW5nY2h1bg_size_16_color_FFFFFF_t_70 1][] 在linux上,当文件大于2G时,在32位上,调用fopen函数会直接返回空。执行结果如下图所示:以Ubuntu\_14\_04\_3.rar为例,第1个窗口显示的是此文件的真实值大小;第2窗口为64位上的执行结果,可见与真实值大小一致;第3个窗口为32位上执行结果,大于2G文件,在32位上不能正常调用fopen函数。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdiaW5nY2h1bg_size_16_color_FFFFFF_t_70 2][] 测试代码如下所示: 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**:[https://github.com/fengbingchun/Messy\_Test][https_github.com_fengbingchun_Messy_Test] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdiaW5nY2h1bg_size_16_color_FFFFFF_t_70]: /images/20221014/1b0448b251ef4a8a88c6145d9a9871c1.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdiaW5nY2h1bg_size_16_color_FFFFFF_t_70 1]: /images/20221014/13e48f63a7b94ac090dd06dfec22789b.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdiaW5nY2h1bg_size_16_color_FFFFFF_t_70 2]: /images/20221014/6eaf01137cb947dd961cfdcc46892116.png [https_github.com_fengbingchun_Messy_Test]: https://github.com/fengbingchun/Messy_Test
还没有评论,来说两句吧...