/* * ============================================================ * C++语言文件读写示例 (cpp_file_io.cpp) * ============================================================ * * C++ 使用 头文件中提供的流(stream)类来操作文件, * 常用的三个类: * std::ifstream —— 输入文件流(input file stream),用于"读" * std::ofstream —— 输出文件流(output file stream),用于"写" * std::fstream —— 输入输出文件流,既能读也能写 * * 这些类都继承自 std::istream / std::ostream,因此可以像操作 * std::cin / std::cout 一样,使用 << 和 >> 运算符读写文件, * 也可以用 getline() 按行读取。 * * 打开模式(第二个参数),用 std::ios 里的枚举值组合(用 | 连接): * std::ios::in 只读 * std::ios::out 只写 * std::ios::app 追加 * std::ios::trunc 打开时清空文件 * std::ios::binary 二进制模式(不做文本转换) * std::ios::ate 打开后立即定位到文件末尾 * * 与C语言最大的不同点(RAII机制): * ifstream/ofstream 是"资源获取即初始化"(RAII)的对象, * 当它离开作用域(比如函数结束、{}代码块结束)时, * 析构函数会自动调用 close(),把缓冲区数据刷新到磁盘, * 不需要像C语言那样手动调用 fclose(),从而避免忘记关闭 * 文件导致的资源泄漏问题。 * * 特点总结: * 1. 面向对象,类型安全:可以直接用 << 写入 int/double/string等, * 不需要像C那样写格式化字符串"%d"。 * 2. 自动管理资源(RAII),无需手动close,异常安全性更好。 * 3. 可以用异常(exceptions)机制处理错误,也可以用状态位(good/fail/eof)判断。 * 4. 相比C的stdio,部分场景下性能略低(多了一层抽象),但差距通常很小, * 且可以通过 sync_with_stdio(false) 等方式优化。 * ============================================================ */ #include // std::cout, std::cerr #include // std::ifstream, std::ofstream, std::fstream #include // std::string, std::getline #include // std::vector 用于存放读取到的数据 #include // std::istringstream 用于按逗号分割字符串 /* 定义一个结构体,与C版本对应,用于演示二进制读写 */ struct Student { int id; char name[32]; // 为了让二进制布局和C版本一致,这里用char数组而不是std::string double score; }; int main() { const std::string text_file = "cpp_demo.txt"; const std::string binary_file = "cpp_demo.bin"; /* --------------------------------------------------------- * 第一部分:文本文件的写入与读取 * --------------------------------------------------------- */ std::cout << "===== [C++] 文本文件读写演示 =====\n"; /* 1. 创建 ofstream 对象即可"打开"文件用于写入 * 默认模式是 std::ios::out,若省略第二参数也可以。 * 与C的"w"模式一样:文件不存在则创建,存在则清空。 */ { std::ofstream ofs(text_file); // 等价于 ofstream ofs(text_file, std::ios::out); if (!ofs.is_open()) { // is_open() 判断文件是否打开成功,类似C语言判断 fp == NULL std::cerr << "打开文件写入失败: " << text_file << std::endl; return 1; } /* 2. 直接使用 << 运算符写入,像 std::cout 一样自然,无需格式化字符串 */ ofs << "Hello from C++!" << "\n"; ofs << 1 << "," << "Alice" << "," << 95.5 << "\n"; ofs << 2 << "," << "Bob" << "," << 88.0 << "\n"; std::cout << "已写入文件: " << text_file << std::endl; /* 3. ofs 在这个花括号 {} 结束时会自动析构,自动调用close()刷新并关闭文件, * 不需要像C语言那样手动 fclose()!这就是RAII的好处。 */ } /* 4. 重新打开文件用于读取,使用 ifstream */ { std::ifstream ifs(text_file); if (!ifs.is_open()) { std::cerr << "打开文件读取失败: " << text_file << std::endl; return 1; } /* 5. 使用 std::getline 按行读取到 std::string 中, * 比C的fgets更安全(不需要担心缓冲区大小、不会保留换行符)。 */ std::string line; std::cout << "--- 按行读取内容 ---\n"; while (std::getline(ifs, line)) { std::cout << "读到一行: " << line << std::endl; } // ifs 离开作用域自动关闭 } /* 6. 使用 istringstream 解析逗号分隔的结构化数据(比C的fscanf更安全灵活) */ { std::ifstream ifs(text_file); std::string line; std::getline(ifs, line); // 跳过第一行 "Hello from C++!" std::cout << "--- 使用istringstream解析结构化数据 ---\n"; while (std::getline(ifs, line)) { std::istringstream iss(line); std::string id_str, name, score_str; if (std::getline(iss, id_str, ',') && std::getline(iss, name, ',') && std::getline(iss, score_str)) { int id = std::stoi(id_str); double score = std::stod(score_str); std::cout << "解析结果: id=" << id << ", name=" << name << ", score=" << score << std::endl; } } } /* --------------------------------------------------------- * 第二部分:二进制文件的写入与读取 * --------------------------------------------------------- */ std::cout << "\n===== [C++] 二进制文件读写演示 =====\n"; std::vector students_out = { {1, "Alice", 95.5}, {2, "Bob", 88.0} }; /* 1. 以二进制写模式打开:std::ios::out | std::ios::binary */ { std::ofstream ofs(binary_file, std::ios::out | std::ios::binary); if (!ofs.is_open()) { std::cerr << "打开二进制文件写入失败" << std::endl; return 1; } /* 2. write(数据地址(需转成char*), 字节数) * reinterpret_cast 把任意类型指针转换为 char* 字节流, * 这是C++里进行二进制IO的标准写法。 */ ofs.write(reinterpret_cast(students_out.data()), sizeof(Student) * students_out.size()); std::cout << "写入了 " << students_out.size() << " 条学生记录\n"; } /* 3. 以二进制读模式打开并读回 */ { std::ifstream ifs(binary_file, std::ios::in | std::ios::binary); if (!ifs.is_open()) { std::cerr << "打开二进制文件读取失败" << std::endl; return 1; } std::vector students_in(2); ifs.read(reinterpret_cast(students_in.data()), sizeof(Student) * students_in.size()); // gcount() 返回上一次read实际读取到的字节数,可用于校验是否读满 std::streamsize actually_read = ifs.gcount(); size_t record_count = actually_read / sizeof(Student); std::cout << "读取了 " << record_count << " 条学生记录:\n"; for (size_t i = 0; i < record_count; i++) { std::cout << " id=" << students_in[i].id << ", name=" << students_in[i].name << ", score=" << students_in[i].score << std::endl; } } /* --------------------------------------------------------- * 第三部分:追加写入演示 (std::ios::app) * --------------------------------------------------------- */ std::cout << "\n===== [C++] 追加写入演示 =====\n"; { std::ofstream ofs(text_file, std::ios::app); // 追加模式,不清空原内容 if (ofs.is_open()) { ofs << 3 << "," << "Charlie" << "," << 77.0 << "\n"; std::cout << "已追加一行到 " << text_file << std::endl; } } /* --------------------------------------------------------- * 第四部分:使用fstream同时读写同一个文件(C++特有的便利类) * --------------------------------------------------------- */ std::cout << "\n===== [C++] fstream 读写同一文件演示 =====\n"; { // in | out 表示既能读也能写;文件必须已存在(不加trunc就不会清空) std::fstream fs(text_file, std::ios::in | std::ios::out); if (fs.is_open()) { std::string first; std::getline(fs, first); std::cout << "fstream读到第一行: " << first << std::endl; // 还可以继续用 fs << ... 写入,实现同一个流对象读写混合操作 } } std::cout << "\nC++语言文件读写演示结束。\n"; return 0; }