大文件读写运行时异常排查:1.8GB及以上文件出错原因
咱们来一步步拆解你代码里的问题,看看为什么处理1.8GB级别的大文件会出现异常:
核心问题分析
1. 32位程序的内存限制(最可能的元凶)
你用的是VS2015,默认新建的项目大概率是**32位(x86)**编译目标。即使在64位Windows系统上,32位进程的虚拟地址空间默认也只有2GB。你要一次性分配1.8GB的连续内存块,这几乎是不可能的——系统内存里的碎片会让你根本申请不到这么大的连续空间,new char[length]会直接抛出std::bad_alloc异常,而你没有任何异常处理逻辑,程序自然就崩了。
2. 类型不匹配导致的未定义行为
在getFileContent函数里,当文件打开失败时你返回了"",这是一个const char*类型的字符串字面量,但你的函数返回值是char*。更致命的是,调用方最后会执行delete fileContent;——删除字符串字面量属于C++里的未定义行为,轻则程序崩溃,重则出现诡异的内存错误。
3. 文件大小计算的双重隐患
file.tellg()返回的是std::streampos类型,直接强转成long long虽然在多数场景能工作,但并不是标准做法,在某些特殊文件系统或平台下可能出现大小计算错误。- 写入文件时你又调用
myClass.getFileSize(fileName)重新计算大小,如果在读取和写入之间原文件被修改,这个大小就会和之前读取的length不一致,导致写入的数据要么截断要么越界。
4. 未检查文件读取是否成功
file.read(strBuffer, length)并不保证一定能读取到全部数据——比如文件中途被截断、IO错误等情况都会导致读取失败。你没有检查read的返回值,也没有用file.gcount()确认实际读取的字节数,这会导致写入的数据可能不完整。
修复方案
第一步:切换到64位编译
打开VS2015的项目属性,把平台工具集改成x64。64位进程的虚拟地址空间大得多(理论上16EB),分配1.8GB的连续内存块就轻松多了。
第二步:修复内存管理和类型问题
建议用std::vector<char>代替手动分配的char数组,它会自动管理内存,避免手动new/delete的问题:
#include <stdexcept> #include <string> #include <vector> std::vector<char> myClass::getFileContent(const char * fileName) { std::ifstream file(fileName, std::ios::binary|std::ios::ate); if (!file.is_open()) { throw std::runtime_error(std::string("Failed to open file: ") + strerror(errno)); } std::streampos length = file.tellg(); if (length == std::streampos(-1)) { throw std::runtime_error("Failed to get file size"); } std::vector<char> buffer(static_cast<size_t>(length)); file.seekg(0, std::ios::beg); if (!file.read(buffer.data(), length)) { throw std::runtime_error("Failed to read file content"); } return buffer; }
第三步:修改写入逻辑
利用vector的大小来获取文件长度,避免重复计算,同时增加异常处理:
try { const char* fileName = "C:\\desktop-amd64.iso"; auto fileContent = myClass.getFileContent(fileName); std::ofstream file("c:\\file.iso", std::ios::binary); if (!file.is_open()) { throw std::runtime_error(std::string("Failed to open output file: ") + strerror(errno)); } file.write(fileContent.data(), fileContent.size()); if (!file) { throw std::runtime_error("Failed to write to output file"); } } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
额外优化:分块读取写入(推荐)
如果不想一次性加载整个大文件到内存,可以用分块的方式,每次读取固定大小的缓冲区(比如4MB),然后写入,这样内存占用会非常低:
#include <stdexcept> #include <string> #include <vector> void myClass::copyFile(const char* srcPath, const char* dstPath) { const size_t BUFFER_SIZE = 4 * 1024 * 1024; // 4MB缓冲区 std::ifstream src(srcPath, std::ios::binary); std::ofstream dst(dstPath, std::ios::binary); if (!src.is_open()) { throw std::runtime_error(std::string("Failed to open source file: ") + strerror(errno)); } if (!dst.is_open()) { throw std::runtime_error(std::string("Failed to open destination file: ") + strerror(errno)); } std::vector<char> buffer(BUFFER_SIZE); while (src.read(buffer.data(), BUFFER_SIZE)) { dst.write(buffer.data(), src.gcount()); } // 处理最后一块不足BUFFER_SIZE的数据 dst.write(buffer.data(), src.gcount()); if (!src.eof() || !dst) { throw std::runtime_error("File copy failed"); } }
内容的提问来源于stack exchange,提问作者Lion King

