一、核心头文件与基础类
1. 必备头文件
<fstream>:所有文件流操作的核心头文件,包含ifstream/ofstream/fstream三类核心文件流类;<iostream>:提供流基类和基础输入输出能力(如cin/cout),文件流类均继承自其基类;<string>:用于处理文件路径字符串、读写的文本内容;<cstdio>:补充文件重命名、删除等高级操作(兼容 C 标准库)。
2. 核心文件流类
std::ifstream:只读文件流,专门用于从文件读取数据(文本 / 二进制均可),默认以ios::in模式打开;std::ofstream:只写文件流,专门用于向文件写入数据(文本 / 二进制均可),默认以ios::out | ios::trunc模式打开;std::fstream:读写文件流,支持同时读取和写入文件,默认以ios::in | ios::out模式打开。
二、文件打开模式
文件打开模式用于指定文件的操作规则,可通过 | 组合多个模式,所有模式均属于 std::ios_base::openmode 类型:
ios::in- 功能:以只读方式打开文件,文件不存在则打开失败;
- 适用场景:读取配置文件、文本内容等只读操作,是
ifstream的默认模式; - 常用组合:
ios::in | ios::binary(二进制只读模式)。
ios::out- 功能:以只写方式打开文件,文件不存在则自动创建,文件存在则清空原有内容;
- 适用场景:写入新文件、覆盖原有文件内容,是
ofstream的默认模式; - 常用组合:
ios::out | ios::app(追加写模式)。
ios::app- 功能:追加写入模式,所有写入内容都会添加到文件末尾,不会清空原有内容;
- 适用场景:日志文件写入、累计数据记录等需要保留历史内容的场景。
ios::binary- 功能:二进制模式打开文件,不处理换行符(Windows 下
\r\n/Linux 下\n)等特殊字符,直接读写原始字节; - 适用场景:图片、视频、自定义结构体等非文本数据的读写。
- 功能:二进制模式打开文件,不处理换行符(Windows 下
ios::trunc- 功能:打开文件时清空原有内容,
ios::out模式默认包含此属性; - 注意:与
ios::app冲突,同时指定时app优先级更高。
- 功能:打开文件时清空原有内容,
ios::ate- 功能:打开文件后,文件指针直接定位到文件末尾,可通过指针操作移动到其他位置;
- 典型用法:快速获取文件总大小(定位到末尾后调用
tellg())。
三、核心函数说明(签名 + 功能 + 示例)
1. 文件打开与关闭
(1)构造函数直接打开
- 签名(以
ifstream为例):std::ifstream::ifstream(const std::string& path, std::ios_base::openmode mode = ios::in); - 功能:创建流对象的同时打开指定路径的文件,一步完成对象初始化和文件打开;
cpp
// 只读打开文本配置文件(默认ios::in模式)
std::ifstream config_in("config.txt");
// 追加模式打开日志文件(ios::out + ios::app)
std::ofstream log_out("app.log", std::ios::out | std::ios::app);
(2)open () 显式打开
- 签名:
void std::ifstream::open(const std::string& path, std::ios_base::openmode mode = ios::in); - 功能:为已创建的空流对象绑定并打开指定文件,适用于需要延迟打开文件的场景;
cpp
std::fstream data_fs;
// 读写+二进制模式打开数据文件
data_fs.open("data.bin", std::ios::in | std::ios::out | std::ios::binary);
(3)is_open () 检查打开状态
- 签名:
bool std::ifstream::is_open() const; - 功能:判断文件是否成功打开,返回
true表示成功,false表示失败(路径错误、权限不足等);
cpp
std::ifstream in("test.txt");
if (!in.is_open()) {
std::cerr << "文件打开失败:test.txt" << std::endl;
return 1; // 异常退出
}
(4)close () 关闭文件
- 签名:
void std::ifstream::close(); - 功能:关闭已打开的文件,释放系统文件句柄资源;
cpp
std::ofstream out("log.txt");
// 写入内容后显式关闭
out.close();
2. 文本文件读写
(1)getline () 逐行读取
- 签名:
std::istream& std::getline(std::istream& is, std::string& str); - 功能:从输入流中读取一行文本(直到换行符,不包含换行符)到字符串变量中;
cpp
std::ifstream in("config.txt");
std::string line;
// 逐行读取直到文件末尾
while (std::getline(in, line)) {
std::cout << "读取行:" << line << std::endl;
}
(2)>> 运算符 按空白符读取
- 功能:从文件流中读取数据(单词、数字等),自动跳过空格、换行、制表符等空白符;
cpp
std::ifstream in("num.txt"); // 文件内容:10 20 30 40
int num;
// 逐个读取整数
while (in >> num) {
std::cout << "读取数字:" << num << std::endl;
}
(3)get () 单字符读取
- 签名:
std::istream& std::istream::get(char& c); - 功能:读取单个字符(包括空白符、换行符)到字符变量中,保留文件原始格式;
cpp
std::ifstream in("test.txt");
char ch;
// 逐字符读取并输出
while (in.get(ch)) {
std::cout << ch;
}
(4)<< 运算符 格式化写入
- 功能:与
std::cout用法完全一致,向文件流写入格式化数据(字符串、数字、浮点数等);
cpp
std::ofstream out("log.txt");
out << "程序启动时间:" << "2026-01-26" << std::endl;
out << "用户ID:" << 1001 << " | 分数:" << 95.5 << std::endl;
(5)put () 单字符写入
- 签名:
std::ostream& std::ostream::put(char c); - 功能:向文件流写入单个字符,支持链式调用;
cpp
std::ofstream out("char.txt");
// 链式写入字符,最终文件内容:Hello\n
out.put('H').put('e').put('l').put('l').put('o').put('\n');
3. 二进制文件读写
(1)read () 二进制读取
- 签名:
std::istream& std::istream::read(char* buffer, std::streamsize count); - 功能:从文件流中读取指定字节数的二进制数据到缓冲区;
cpp
// 读取4字节整数(二进制模式)
std::ifstream in("data.bin", std::ios::in | std::ios::binary);
int num;
// 强制转换为char*,读取int大小的字节数
in.read(reinterpret_cast<char*>(&num), sizeof(num));
std::cout << "读取的整数:" << num << std::endl;
(2)write () 二进制写入
- 签名:
std::ostream& std::ostream::write(const char* buffer, std::streamsize count); - 功能:将缓冲区中的指定字节数的二进制数据写入文件流;
cpp
// 写入自定义结构体(二进制模式)
struct Student { int id; char name[20]; float score; };
std::ofstream out("stu.bin", std::ios::out | std::ios::binary);
Student s = {1001, "张三", 95.5f};
out.write(reinterpret_cast<const char*>(&s), sizeof(s));
4. 文件指针与随机访问
文件流通过「读指针(get)」和「写指针(put)」实现随机访问,读指针操作函数以 g 结尾,写指针以 p 结尾:
(1)seekg () 移动读指针
- 签名 1(绝对位置):
std::istream& std::istream::seekg(std::streampos pos); - 签名 2(相对位置):
std::istream& std::istream::seekg(std::streamoff off, std::ios_base::seekdir dir); - 功能:移动读指针到指定位置,
dir可选基准位置:ios::beg:从文件开头偏移;ios::cur:从当前指针位置偏移;ios::end:从文件末尾偏移;
cpp
std::ifstream in("test.txt");
in.seekg(0, std::ios::end); // 指针移到文件末尾(用于获取文件大小)
in.seekg(10, std::ios::beg); // 指针移到文件开头后第10字节
(2)tellg () 获取读指针位置
- 签名:
std::streampos std::istream::tellg() const; - 功能:返回当前读指针的位置(字节数,从文件开头算起);
cpp
std::ifstream in("test.txt");
in.seekg(0, std::ios::end);
std::streampos file_size = in.tellg(); // 获取文件总字节数
std::cout << "文件大小:" << file_size << " 字节" << std::endl;
(3)seekp () 移动写指针
- 功能与签名同
seekg(),区别是操作写指针,适用于写文件或读写文件场景;
cpp
std::fstream fs("data.bin", std::ios::in | std::ios::out | std::ios::binary);
fs.seekp(5); // 写指针移到第5字节位置(绝对位置)
(4)tellp () 获取写指针位置
- 功能与签名同
tellg(),区别是返回写指针的当前位置;
cpp
std::ofstream out("test.txt");
out << "Hello";
std::streampos pos = out.tellp(); // 返回5("Hello"共5字节)
5. 错误处理函数
文件操作需检查流状态,避免操作失败导致程序异常:
(1)eof()
- 签名:
bool std::ios::eof() const; - 功能:判断是否到达文件末尾(EOF),返回
true表示正常读取到末尾;
cpp
std::ifstream in("test.txt");
std::string line;
while (std::getline(in, line)) {}
if (in.eof()) {
std::cout << "文件读取完成(正常到达末尾)" << std::endl;
}
(2)fail()
- 签名:
bool std::ios::fail() const; - 功能:判断是否发生非致命错误(如格式错误:读取整数时读到字母);
cpp
if (in.fail()) {
std::cerr << "文件读取格式错误(非致命)" << std::endl;
}
(3)bad()
- 签名:
bool std::ios::bad() const; - 功能:判断是否发生致命错误(如文件损坏、磁盘故障、流被破坏);
cpp
if (in.bad()) {
std::cerr << "文件读取致命错误(如文件损坏)" << std::endl;
}
(4)clear()
- 签名:
void std::ios::clear(std::ios_base::iostate state = std::ios_base::goodbit); - 功能:清除流的错误状态(如
eof()为true后,需调用clear()才能继续操作);
cpp
in.clear(); // 清除所有错误状态
in.seekg(0); // 重置读指针到文件开头
6. 高级文件操作
(1)文件重命名
- 函数:
std::rename(来自<cstdio>); - 功能:修改文件名,成功返回 0,失败返回非 0;
cpp
#include <cstdio>
if (std::rename("old.txt", "new.txt") != 0) {
std::cerr << "文件重命名失败" << std::endl;
}
(2)文件删除
- 函数:
std::remove(来自<cstdio>); - 功能:删除指定文件,成功返回 0,失败返回非 0;
cpp
#include <cstdio>
if (std::remove("tmp.txt") != 0) {
std::cerr << "文件删除失败" << std::endl;
}
(3)获取文件大小
- 实现方式:结合
seekg()和tellg();
cpp
std::ifstream in("test.bin", std::ios::binary);
in.seekg(0, std::ios::end); // 指针移到末尾
int size = in.tellg(); // 获取字节数
in.seekg(0, std::ios::beg); // 移回开头,方便后续操作
std::cout << "文件大小:" << size << " 字节" << std::endl;
四、常用场景完整示例
场景 1:逐行读取文本配置文件
cpp
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
// 打开配置文件
ifstream config_in("config.conf");
if (!config_in.is_open()) {
cerr << "配置文件打开失败:config.conf" << endl;
return 1;
}
// 逐行读取并输出
string line;
cout << "===== 配置文件内容 =====" << endl;
while (getline(config_in, line)) {
cout << line << endl;
}
// 检查读取状态
if (config_in.eof()) {
cout << "\n配置文件读取完成" << endl;
}
config_in.close();
return 0;
}
场景 2:追加写入日志文件
cpp
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
// 追加模式打开日志文件
ofstream log_out("app.log", ios::out | ios::app);
if (!log_out) {
cerr << "日志文件打开失败" << endl;
return 1;
}
// 写入日志内容
log_out << "【INFO】程序启动成功 | 时间:2026-01-26 15:30:00" << endl;
log_out << "【DEBUG】用户ID:1001 | 操作:登录" << endl;
log_out.close();
cout << "日志写入完成" << endl;
return 0;
}
场景 3:二进制读写结构体数据
cpp
#include <fstream>
#include <iostream>
using namespace std;
// 自定义数据结构体
struct UserData {
int id;
char username[16];
double balance;
};
int main() {
// 1. 写入二进制数据
ofstream out("user_data.bin", ios::out | ios::binary);
if (!out) {
cerr << "写入文件打开失败" << endl;
return 1;
}
UserData user = {1002, "李四", 5000.50};
out.write(reinterpret_cast<const char*>(&user), sizeof(user));
out.close();
// 2. 读取二进制数据
ifstream in("user_data.bin", ios::in | ios::binary);
if (!in) {
cerr << "读取文件打开失败" << endl;
return 1;
}
UserData read_user;
in.read(reinterpret_cast<char*>(&read_user), sizeof(read_user));
in.close();
// 输出读取结果
cout << "用户ID:" << read_user.id << endl;
cout << "用户名:" << read_user.username << endl;
cout << "余额:" << read_user.balance << endl;
return 0;
}
场景 4:批量打开并读取多个文件
cpp
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
// 待打开的文件列表
vector<string> file_list = {"file1.txt", "file2.txt", "file3.txt"};
// 存储多个读流对象(使用移动语义,ifstream不可拷贝)
vector<ifstream> file_ins;
// 批量打开文件
for (const string& path : file_list) {
ifstream in(path);
if (!in) {
cerr << "跳过失败文件:" << path << endl;
continue;
}
file_ins.push_back(move(in)); // 移动语义存入容器
}
// 批量读取每个文件
for (int i = 0; i < file_ins.size(); ++i) {
cout << "\n===== 读取文件:" << file_list[i] << " =====" << endl;
string line;
while (getline(file_ins[i], line)) {
cout << line << endl;
}
file_ins[i].close();
}
return 0;
}
五、关键注意事项
- 打开文件后必须检查
is_open():路径错误、权限不足、文件不存在等都会导致打开失败,未检查会引发后续操作崩溃; - 流对象不可拷贝:
ifstream/ofstream/fstream均禁用拷贝构造和赋值,批量管理时需用std::move实现移动; - 二进制模式必加
ios::binary:读写图片、结构体等非文本数据时,不加此模式会导致换行符被篡改,数据错误; - 错误状态需清除:调用
eof()/fail()为true后,流会进入错误状态,需用clear()清除后才能继续操作; - 及时关闭文件:虽然流对象析构时会自动关闭文件,但显式调用
close()能及时释放文件句柄,避免资源泄露; - 路径分隔符:跨平台代码优先使用
/(Windows 和 Linux 均支持),避免\(Windows 专属,需转义为\\)。
总结
- 文件操作核心是「流对象 + 打开模式 + 读写方法」,选对类(ifstream/ofstream/fstream)和模式是基础;
- 文本文件用
getline()/<<读写,二进制文件用read()/write()+ios::binary; - 随机访问依赖
seekg()/tellg()/seekp()/tellp(),错误处理需结合eof()/fail()/clear(); - 批量操作多个文件时,用
vector存储流对象并配合移动语义,避免拷贝错误。