C++ 文件写操作

📅 2026/7/17 11:41:13
C++ 文件写操作
结合前文讨论的二进制读写及含指针结构体的序列化背景C 文件写操作主要分为‌文本写入‌和‌二进制写入‌两类。以下是核心实现方式与关键注意事项1. 文本文件写入#include fstream #include iostream void writeText() { // ios::out 为默认模式ios::app 表示追加 std::ofstream out(log.txt, std::ios::out); if (out.is_open()) { out ID: 101 std::endl; out Status: Active std::endl; out.close(); } }‌特点‌自动处理换行符转换Windows 下\n转\r\n数据以 ASCII 存储。‌注意‌不适合存储浮点数高精度数据或复杂二进制结构。2. 二进制文件写入适用于图像、音频、序列化对象等场景使用write方法直接内存拷贝。void writeBinary() { int id 101; float score 95.5f; // 必须指定 ios::binary std::ofstream out(data.bin, std::ios::binary); if (out.is_open()) { // 强转为 const char*并指定字节数 out.write(reinterpret_castconst char*(id), sizeof(int)); out.write(reinterpret_castconst char*(score), sizeof(float)); out.close(); } }‌特点‌数据在内存中如何存储文件中就如何保存无格式转换效率高。‌注意‌不同平台间可能存在字节序Endianness和对齐Padding差异。3. 含指针/变长数据的写入序列化结合前文提到的“含指针结构体”问题不能直接write整个对象需‌手动序列化‌先写长度/标识再写实际内容。void writeComplexData(const std::string name, const int* arr, int count) { std::ofstream out(complex.bin, std::ios::binary); // 1. 写字符串先写长度再写内容 int len static_castint(name.length()); out.write(reinterpret_castconst char*(len), sizeof(int)); out.write(name.c_str(), len); // 2. 写数组先写个数再写数据块 out.write(reinterpret_castconst char*(count), sizeof(int)); if (count 0) { out.write(reinterpret_castconst char*(arr), count * sizeof(int)); } out.close(); }4. 关键最佳实践‌检查流状态‌每次重要写入后建议检查out.fail()或out.bad()确保磁盘空间充足且权限正确。‌资源管理‌推荐使用 RAII 机制让std::ofstream在作用域结束时自动关闭文件避免忘记close()导致数据未刷入磁盘。‌刷新缓冲区‌若需确保数据立即落盘如崩溃恢复场景调用out.flush()。‌模式选择‌ios::trunc默认覆盖原文件。ios::app追加到文件末尾。ios::ate打开后定位到末尾但可随机写入。5补充更多 C 文件写操作的实战示例前文讨论的二进制读写、含指针结构体序列化及ios::binary标志的重要性以下补充更多 C 文件写操作的实战示例覆盖追加写入、随机访问写入、异常安全写入及大文件分块写入场景。一、追加模式写入日志记录场景在已有文件末尾添加数据而不覆盖原内容需使用std::ios::app标志。常用于日志系统或数据累积。#include fstream #include iostream #include string void appendLog(const std::string message) { // ios::app 确保每次写入都定位到文件末尾 std::ofstream logFile(system.log, std::ios::out | std::ios::app); if (logFile.is_open()) { logFile [INFO] message std::endl; // 日志通常要求实时落盘防止崩溃丢失 logFile.flush(); } else { std::cerr 无法打开日志文件 std::endl; } }关键点‌ios::app模式下即使手动调用seekp改变位置写入时仍会强制回到末尾保证数据不交错。二、随机访问写入修改特定位置数据使用seekp移动写指针实现类似数据库“更新记录”的功能。适用于固定长度记录的文件修改。void updateRecordAtPosition(const std::string filename, long pos, int newValue) { // ios::in | ios::out 允许同时读写ios::binary 保证字节对齐 std::fstream file(filename, std::ios::in | std::ios::out | std::ios::binary); if (file.is_open()) { // 将写指针移动到指定字节偏移量 file.seekp(pos); if (file.good()) { // 在当前位置写入新的整数值 file.write(reinterpret_castconst char*(newValue), sizeof(int)); file.flush(); // 确保修改立即生效 } file.close(); } }关键点‌仅适用于定长数据。若新数据长度与原数据不同会破坏后续数据结构此时应改用“读取-修改-重写整个文件”的策略。三、异常安全的 RAII 写入利用 C 作用域机制确保即使发生异常文件也能正确关闭并释放资源。这是现代 C 推荐的标准写法。#include stdexcept void safeWriteData(const std::string filename, const int* data, size_t count) { // 构造函数中打开文件析构函数中自动关闭 std::ofstream out(filename, std::ios::binary); if (!out) { throw std::runtime_error(文件打开失败: filename); } // 执行写入操作 out.write(reinterpret_castconst char*(data), count * sizeof(int)); // 检查写入是否成功 if (!out) { throw std::runtime_error(写入过程中发生错误); } // 离开作用域时out 自动调用 close()无需手动管理 }关键点‌避免使用裸指针管理文件句柄防止因中途return或throw导致文件未关闭、数据损坏或资源泄漏。四、大文件分块写入内存优化当数据量极大如 GB 级图像或视频时一次性加载到内存会导致溢出。应采用分块循环写入。void writeLargeFile(const std::string filename, const char* sourceBuffer, size_t totalSize) { std::ofstream out(filename, std::ios::binary); if (!out) return; const size_t CHUNK_SIZE 4096; // 每次写入 4KB size_t offset 0; while (offset totalSize) { // 计算本次实际要写的字节数 size_t bytesToWrite std::min(CHUNK_SIZE, totalSize - offset); out.write(sourceBuffer offset, bytesToWrite); if (out.fail()) { std::cerr 磁盘写入失败 std::endl; break; } offset bytesToWrite; } out.close(); }关键点‌通过控制CHUNK_SIZE平衡内存占用与 I/O 效率。对于网络传输或极慢磁盘可适当增大块大小以减少系统调用次数。五、写入前的目录检查在写入文件前确保目标目录存在避免因路径不存在导致写入失败。#include filesystem // C17 void writeWithDirCheck(const std::string filePath, const std::string content) { std::filesystem::path path(filePath); // 创建所有缺失的父目录 if (!path.parent_path().empty()) { std::filesystem::create_directories(path.parent_path()); } std::ofstream out(filePath); if (out) { out content; } }关键点‌std::filesystem::create_directories是幂等的若目录已存在则不执行任何操作安全且简洁。六. 文本文件写入适用于日志、配置文件等人类可读场景使用运算符进行格式化输出。#include fstream #includeiostream using namespace std; void test01(){ ofstream ofs; ofs.open(test.txt,ios :: out); ofs 姓名:张三 endl; ofs 性别:男 endl; ofs 年龄: 18 endl; ofs.close(); } int main() { test01(); system( pause); }这个就是文件的内容默认写进和这个cpp同一层路径1、基础文本写入示例使用std::ofstream配合运算符数据会自动转换为字符序列存储。#include fstream #include iostream void writeTextFile() { // 默认模式为 ios::out | ios::trunc (覆盖写入) std::ofstream out(report.txt); if (out.is_open()) { int id 101; double score 98.5; // 格式化输出自动处理空格和换行 out ID: id std::endl; out Score: score std::endl; out.close(); } }特点‌代码直观支持多种数据类型混合输出。‌注意‌std::endl不仅插入换行符\n还会强制刷新缓冲区flush频繁使用会影响性能普通换行建议直接用\n。2、追加模式写入日志场景若需在不删除原内容的基础上增加数据必须指定std::ios::app标志。void appendLog(const std::string msg) { // ios::app 确保写指针始终位于文件末尾 std::ofstream log(app.log, std::ios::app); if (log) { log [LOG] msg \n; // 日志建议手动 flush 确保崩溃时数据不丢失 log.flush(); } }3、文本写入的关键注意事项‌换行符转换‌在 Windows 系统中文本模式下写入\n会被自动转换为\r\n。若需跨平台兼容或处理原始字节请勿使用文本模式。‌精度控制‌浮点数默认只输出 6 位有效数字。若需高精度需配合iomanip使用#include iomanip out std::fixed std::setprecision(10) 3.1415926535;‌编码问题‌C 标准库默认使用系统本地编码如 GBK 或 UTF-8。若需写入特定编码如 UTF-8 BOM需手动写入头部字节或使用第三方库。‌性能优化‌对于大量小数据写入建议先存入std::string或缓冲区再一次性write或到文件减少系统 I/O 调用次数。