C++内存映射写入文件无法扩容的技术问题咨询
文件写入后未扩容导致内容截断的解决方案
问题本质
你遇到的核心问题是:当写入位置加上待写入数据的总长度超过目标文件原有大小时,操作系统不会自动扩展文件尺寸——内存映射中能看到完整数据只是因为修改在内存页中,但磁盘上的文件未被扩容,超出原大小的内容不会被持久化。
解决思路
必须在写入数据之前手动计算目标文件需要的最小尺寸,提前扩容文件,确保有足够空间容纳所有写入内容。以下是两种常见场景的实现方案:
方案1:标准C++文件操作实现扩容+写入
#include <fstream> #include <filesystem> #include <vector> #include <cstdlib> int main(int argc, char* argv[]) { if (argc != 4) return 1; const char* target_path = argv[1]; off_t write_offset = std::stoll(argv[2]); const char* source_path = argv[3]; // 读取待写入的全部数据 std::ifstream source_file(source_path, std::ios::binary | std::ios::ate); std::streamsize source_size = source_file.tellg(); source_file.seekg(0); std::vector<char> source_data(source_size); source_file.read(source_data.data(), source_size); // 计算目标文件需要的最小尺寸 std::uintmax_t target_current_size = std::filesystem::file_size(target_path); std::uintmax_t required_size = write_offset + source_size; // 若需要则扩容文件 if (required_size > target_current_size) { std::ofstream resize_file(target_path, std::ios::binary | std::ios::app); resize_file.seekp(required_size - 1); resize_file.write("", 1); // 通过写入空字节扩展文件到指定大小 resize_file.close(); } // 写入数据到目标位置 std::fstream target_file(target_path, std::ios::binary | std::ios::in | std::ios::out); target_file.seekp(write_offset); target_file.write(source_data.data(), source_size); target_file.close(); return 0; }
方案2:内存映射(mmap)场景下的扩容+写入(Linux/macOS)
如果你的代码基于内存映射实现,需先通过ftruncate扩容文件,再进行映射和写入:
#include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include <vector> #include <cstdlib> #include <cstring> #include <iostream> int main(int argc, char* argv[]) { if (argc != 4) return 1; const char* target_path = argv[1]; off_t write_offset = std::stoll(argv[2]); const char* source_path = argv[3]; // 读取源文件数据 int source_fd = open(source_path, O_RDONLY); struct stat source_stat; fstat(source_fd, &source_stat); std::vector<char> source_data(source_stat.st_size); read(source_fd, source_data.data(), source_stat.st_size); close(source_fd); // 打开目标文件并获取当前大小 int target_fd = open(target_path, O_RDWR); struct stat target_stat; fstat(target_fd, &target_stat); // 扩容文件到所需大小 off_t required_size = write_offset + source_stat.st_size; if (required_size > target_stat.st_size) { if (ftruncate(target_fd, required_size) == -1) { perror("ftruncate failed"); close(target_fd); return 1; } } // 内存映射目标文件 void* target_map = mmap(nullptr, required_size, PROT_READ | PROT_WRITE, MAP_SHARED, target_fd, 0); if (target_map == MAP_FAILED) { perror("mmap failed"); close(target_fd); return 1; } // 写入数据到映射区域的指定位置 memcpy(static_cast<char*>(target_map) + write_offset, source_data.data(), source_stat.st_size); // 同步映射内容到磁盘并释放资源 msync(target_map, required_size, MS_SYNC); munmap(target_map, required_size); close(target_fd); return 0; }
关键注意事项
- 必须先扩容再写入:无论用哪种方式,都要确保文件尺寸提前满足写入需求,否则超出原大小的内容会被丢弃。
- 用二进制模式打开文件:避免文本模式下的换行符转换导致长度计算错误。
- 内存映射场景需同步数据:写入映射区域后调用
msync,确保内存中的修改刷入磁盘。
内容的提问来源于stack exchange,提问作者Many Questions
相关产品推荐
相关产品推荐

