文件流操作
定义与作用
文件流将文件抽象为流,使文件读写与标准 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()返回 falseofstream默认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 read | readsome 返回立即可用的数据量 |