乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

文件流操作

定义与作用

文件流将文件抽象为流,使文件读写与标准 IO 使用一致的接口。三种核心类:

std::ifstream  fin("data.txt");      // 只读文件
std::ofstream  fout("out.txt");      // 只写文件
std::fstream   fio("data.txt", std::ios::in | std::ios::out);  // 读写

核心原理

打开模式

文件流生命周期

完整示例

示例一:飞翔科技配置文件读写

场景说明:李眉用文件流读写飞翔科技的服务器配置文件。

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <sstream>

// 读写飞翔科技配置
bool readConfig(const std::string& path,
                std::map<std::string, std::string>& config) {
    std::ifstream fin(path);
    if (!fin) {
        std::cerr << "无法打开: " << path << "\n";
        return false;
    }

    std::string line;
    while (std::getline(fin, line)) {
        // 跳过空行和注释
        if (line.empty() || line[0] == '#') continue;

        auto pos = line.find('=');
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string val = line.substr(pos + 1);
            config[key] = val;
        }
    }
    return true;
}

bool writeConfig(const std::string& path,
                 const std::map<std::string, std::string>& config) {
    std::ofstream fout(path);
    if (!fout) return false;

    fout << "# 飞翔科技服务器配置\n";
    fout << "# 生成时间: 2026-06-14\n\n";
    for (const auto& [key, val] : config)
        fout << key << "=" << val << "\n";
    return true;
}

int main() {
    std::string configPath = "C:/Users/AOXIANG/Desktop/Cpp/指南/13_IO流与文件/feixiang.conf";

    // 1. 写入初始配置
    std::map<std::string, std::string> config = {
        {"server_name", "飞翔科技-主站"},
        {"port", "8080"},
        {"max_connections", "10000"},
        {"log_level", "INFO"},
        {"db_host", "192.168.1.100"},
    };

    if (writeConfig(configPath, config))
        std::cout << "配置已写入: " << configPath << "\n";
    else
        std::cerr << "写入失败\n";

    // 2. 读取验证
    std::map<std::string, std::string> loaded;
    if (readConfig(configPath, loaded)) {
        std::cout << "\n读取配置:\n";
        for (const auto& [k, v] : loaded)
            std::cout << "  " << k << " = " << v << "\n";
    }

    // 3. 追加模式添加新配置
    std::ofstream fout(configPath, std::ios::app);
    fout << "cache_ttl=3600\n";
    fout << "enable_monitoring=true\n";
    fout.close();
    std::cout << "\n已追加 2 条配置\n";

    // 4. 重新读取验证追加
    loaded.clear();
    readConfig(configPath, loaded);
    std::cout << "\n最终配置 (" << loaded.size() << " 项):\n";
    for (const auto& [k, v] : loaded)
        std::cout << "  " << k << " = " << v << "\n";
}

预期输出:

配置已写入: C:/Users/AOXIANG/Desktop/Cpp/指南/13_IO流与文件/feixiang.conf

读取配置:
  db_host = 192.168.1.100
  log_level = INFO
  max_connections = 10000
  port = 8080
  server_name = 飞翔科技-主站

已追加 2 条配置

最终配置 (7 项):
  cache_ttl = 3600
  db_host = 192.168.1.100
  enable_monitoring = true
  log_level = INFO
  max_connections = 10000
  port = 8080
  server_name = 飞翔科技-主站

逐段分析:

  • ifstream 默认 in 模式,文件不存在则 is_open() 返回 false
  • ofstream 默认 out | trunc,打开即截断;app 模式追加内容
  • fout.close() 显式关闭(析构时也会自动关闭)
  • getline(fin, line) 逐行读取,自动处理行尾

示例二:二进制文件读写——日志归档

场景说明:小崔用二进制模式高效读写飞翔科技的日志索引文件。

#include <iostream>
#include <fstream>
#include <cstring>
#include <vector>

// 固定长度记录,方便二进制随机访问
#pragma pack(push, 1)
struct LogIndex {
    char     timestamp[20];   // "2026-06-14 09:00:00"
    int64_t  offset;          // 日志文件中的偏移
    int32_t  length;          // 日志条目长度
    int32_t  level;           // 0=INFO, 1=WARN, 2=ERROR
};
#pragma pack(pop)

void printIndex(const LogIndex& idx) {
    std::cout << "  " << idx.timestamp
              << " | offset=" << idx.offset
              << " | len=" << idx.length
              << " | level=" << idx.level << "\n";
}

int main() {
    std::string binaryPath =
        "C:/Users/AOXIANG/Desktop/Cpp/指南/13_IO流与文件/feixiang_log.idx";

    // 1. 写入二进制索引
    std::vector<LogIndex> indices = {
        {"2026-06-14 09:00:00", 0,      256,  0},
        {"2026-06-14 09:05:12", 256,    512,  0},
        {"2026-06-14 09:10:45", 768,    128,  1},
        {"2026-06-14 09:15:33", 896,    1024, 0},
        {"2026-06-14 09:20:01", 1920,   64,   2},
    };

    {
        std::ofstream fout(binaryPath, std::ios::binary);
        if (!fout) {
            std::cerr << "无法创建文件\n";
            return 1;
        }

        fout.write(reinterpret_cast<const char*>(indices.data()),
                   indices.size() * sizeof(LogIndex));
        fout.close();
        std::cout << "二进制索引写入: " << indices.size() << " 条记录\n";
    }

    // 2. 顺序读取所有记录
    {
        std::ifstream fin(binaryPath, std::ios::binary);
        std::cout << "\n顺序读取:\n";
        LogIndex idx;
        while (fin.read(reinterpret_cast<char*>(&idx), sizeof(LogIndex)))
            printIndex(idx);
    }

    // 3. 随机访问:跳到第 3 条记录
    {
        std::ifstream fin(binaryPath, std::ios::binary);
        fin.seekg(2 * sizeof(LogIndex), std::ios::beg);  // 第3条(0-based索引2)
        LogIndex idx;
        fin.read(reinterpret_cast<char*>(&idx), sizeof(LogIndex));
        std::cout << "\n随机访问(第3条):\n";
        printIndex(idx);

        // 获取当前读取位置
        auto pos = fin.tellg();
        std::cout << "  当前文件指针位置: " << pos << " 字节\n";
    }

    // 4. 筛选 ERROR 级别(level=2)
    {
        std::ifstream fin(binaryPath, std::ios::binary);
        std::cout << "\n筛选 ERROR 级别:\n";
        LogIndex idx;
        while (fin.read(reinterpret_cast<char*>(&idx), sizeof(LogIndex))) {
            if (idx.level == 2) printIndex(idx);
        }
    }
}

预期输出:

二进制索引写入: 5 条记录

顺序读取:
  2026-06-14 09:00:00 | offset=0 | len=256 | level=0
  2026-06-14 09:05:12 | offset=256 | len=512 | level=0
  2026-06-14 09:10:45 | offset=768 | len=128 | level=1
  2026-06-14 09:15:33 | offset=896 | len=1024 | level=0
  2026-06-14 09:20:01 | offset=1920 | len=64 | level=2

随机访问(第3条):
  2026-06-14 09:10:45 | offset=768 | len=128 | level=1
  当前文件指针位置: 120 字节

筛选 ERROR 级别:
  2026-06-14 09:20:01 | offset=1920 | len=64 | level=2

逐段分析:

  • std::ios::binary 阻止系统做换行符转换(\n ↔ \r\n),保证跨平台一致
  • reinterpret_cast<const char*> 将结构体字节视为原始数据进行读写
  • seekg 移动读取位置,tellg 获取当前位置
  • #pragma pack(push, 1) 消除对齐填充,确保 sizeof(LogIndex) 可预测
  • 固定长度记录的二进制文件支持 O(1) 随机访问

易错场景与面试考点

易错场景

1. 忘记 binary 模式

// ❌ 文本模式在 Windows 上会将 \n 转为 \r\n,破坏二进制数据
std::ofstream fout("data.bin");
fout.write(data, size);

// ✅
std::ofstream fout("data.bin", std::ios::binary);

2. ofstream 不检查打开成功

std::ofstream fout("/root/protected/config.txt");
// 没有 if (!fout) 检查,直接写入 → 静默失败

3. 使用 seekg 后未清 eofbit

fin.seekg(0);  // 重置位置
// fin.clear();  // 必须:seekg 不自动清除 eofbit!
std::string line;
std::getline(fin, line);  // 如果不清 eofbit 可能失败

面试考点

考点要点
文件流三大类ifstream(读), ofstream(写), fstream(读写)
打开模式组合in/out/app/ate/trunc/binary
二进制 vs 文本binary 阻止换行符转换
seekg/seekp读/写位置指针移动,支持 beg/cur/end 三个方向
tellg/tellp获取当前读/写位置
RAII 自动关闭文件流析构时自动 close,无需手动
readsome vs readreadsome 返回立即可用的数据量
上一页
格式化输出控制
下一页
字符串流