若要把计算结果、配置或日志保存到磁盘,并在下次运行或其他程序中读回,应使用文件而非仅依赖内存。C++ 通过 <fstream> 将iostream 的流模型扩展到磁盘:std::ifstream 读、std::ofstream 写、std::fstream 读写。与 C 语言文件操作 相比,流对象在析构时自动关闭,并支持与格式化、std::string 的自然配合。打开失败时的处理可与异常或返回值检查结合。
使用文件的目的
- 持久化:程序退出后数据仍可保留。
- 批量数据:大量记录可一次读入处理,无需交互输入。
- 交换:通过复制文件在不同环境或语言之间传递文本或二进制数据。
文本与二进制
- 文本模式
- 按字符/行处理,换行符可能随平台转换;适合日志、配置、CSV 等人类可读内容。
- 二进制模式
- 按字节读写,不做换行转换;适合保存数值数组、结构体块或自定义格式(需注意对齐与可移植性)。
写入并读回
下列程序先写入 notes.txt,关闭后再逐行读出。本站在线编译器不提供磁盘文件,请在本地用 g++ 编译运行。成功时输出为:
#include <fstream>
#include <iostream>
#include <string>
int main() {
{
std::ofstream out("notes.txt");
if (!out) {
std::cerr << "open failed\n";
return 1;
}
out << "line one\n";
out << "The number is " << 42 << '\n';
}
std::ifstream in("notes.txt");
if (!in) {
std::cerr << "read failed\n";
return 1;
}
std::string line;
while (std::getline(in, line)) {
std::cout << line << '\n';
}
return 0;
}输出:
line one
The number is 42内层块结束时 ofstream 析构并关闭文件,随后的 ifstream 才能读到完整内容。流在布尔上下文中为假表示打开或读写失败(如权限不足、路径无效)。长期运行的程序宜在写完后及时 close() 或让对象离开作用域以刷新缓冲。
读取文本文件
逐行读取常用 std::getline,适合含空格的整行内容:
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::ifstream in("notes.txt");
if (!in) {
std::cerr << "notes.txt failed to open\n";
return 1;
}
std::string line;
while (std::getline(in, line)) {
std::cout << line << '\n';
}
if (in.bad()) {
std::cerr << "read error\n";
}
return 0;
}- 到达文件尾时
getline使流进入 eof 状态,循环正常结束。 - 最后一行无换行符时仍能读入该行内容。
- 用
operator>>读单词时,空白会作为分隔符,行为类似从 cin 读取。
打开模式
构造流或调用 open 时可组合标准库模式标志(与 C 的 fopen 模式对应关系如下):
| C++ 常用组合 | 含义 | 文件不存在时 |
|---|---|---|
std::ios::in(ifstream 默认) | 只读 | 打开失败 |
std::ios::out(ofstream 默认) | 只写,截断已有内容 | 新建 |
std::ios::app | 追加到末尾 | 新建 |
std::ios::binary | 二进制(与上组合) | 同文本规则 |
std::ios::trunc | 打开时清空 | 新建 |
std::ofstream log("app.log", std::ios::app);
std::fstream both("data.bin", std::ios::in | std::ios::out | std::ios::binary);Windows 路径中的反斜杠在字符串中应写成 \\,或改用正斜杠 /(多数实现接受)。
二进制读写入门
对 POD 类型或字节缓冲区可使用 read / write:
#include <fstream>
#include <cstdint>
struct Record { std::int32_t id; double value; };
int main() {
Record r{1, 3.14};
std::ofstream out("rec.bin", std::ios::binary);
out.write(reinterpret_cast<const char*>(&r), sizeof(r));
out.close();
Record in{};
std::ifstream fin("rec.bin", std::ios::binary);
fin.read(reinterpret_cast<char*>(&in), sizeof(in));
return 0;
}跨平台或含 std::string 等非 POD 成员时,不宜直接 write 整个对象;应定义序列化格式或使用文本/专用库。
流状态与异常
除 if (!stream) 外,可查询 eof、fail、bad 等状态。调用 exceptions() 可在失败时抛出 std::ios_base::failure,与项目统一的异常策略一致;许多代码仅在打开与关键读写后检查布尔值即可。
| 成员 / 用法 | 作用 |
|---|---|
clear() / ignore() | 恢复流状态、跳过错误输入(配合 cin 时常见) |
seekg / seekp | 移动读/写位置(随机访问) |
tellg / tellp | 查询当前位置 |
文件操作的一般步骤为:构造或 open 并检查是否成功、读写、关闭(或依赖析构)。本节为持久化数据打下基础。下一节介绍移动语义与右值引用:减少不必要的拷贝。