若要把计算结果、配置或日志保存到磁盘,并在下次运行或其他程序中读回,应使用文件而非仅依赖内存。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 并检查是否成功、读写、关闭(或依赖析构)。本节为持久化数据打下基础。下一节介绍移动语义与右值引用:减少不必要的拷贝。