在 C++ 标准库中,文件流类 (定义在 <fstream>
头文件中)用于实现对文件的输入输出(I/O)操作,允许程序读写磁盘文件。它们继承自标准 I/O 流类(如 std::istream
、std::ostream
),因此支持与 cin
/cout
类似的操作方式(如 >>
、<<
运算符、getline
等)。
一、核心文件流类
根据操作方向(读/写)和功能,文件流类主要分为以下三类:
类名 | 继承关系 | 功能描述 | 典型用途 |
---|---|---|---|
std::ifstream |
继承自 std::istream |
输入文件流:仅用于从文件读取数据 | 打开文本/二进制文件并读取内容 |
std::ofstream |
继承自 std::ostream |
输出文件流:仅用于向文件写入数据 | 创建/覆盖文件并写入内容 |
std::fstream |
继承自 std::iostream |
双向文件流:既可读又可写 | 需要同时读写同一文件的场景 |
二、文件打开与关闭
使用文件流类操作文件的基本流程是:创建流对象 → 打开文件 → 读写操作 → 关闭文件。
1. 打开文件的两种方式
- 通过构造函数打开:创建流对象时直接指定文件名和打开模式。
- 通过
open()
成员函数打开 :先创建流对象,再调用open()
打开文件。
两种方式都需要指定 打开模式 (控制文件的读写权限、是否覆盖等),常用模式定义在 <ios>
头文件中(可通过 |
组合):
打开模式标志 | 含义 |
---|---|
std::ios::in |
以"读"模式打开文件(ifstream 默认) |
std::ios::out |
以"写"模式打开文件(ofstream 默认) |
std::ios::app |
以"追加"模式打开:写入数据追加到文件末尾 |
std::ios::trunc |
打开文件时清空原有内容(ofstream 默认) |
std::ios::binary |
以"二进制模式"打开(默认是文本模式) |
std::ios::ate |
打开文件后定位到文件末尾 |
2. 关闭文件
文件操作完成后需调用 close()
成员函数关闭文件,释放文件资源(流对象销毁时会自动调用 close()
,但手动关闭更规范)。
三、常用操作示例
1. std::ofstream
:写入文件
用于创建文件并写入内容,默认模式是 ios::out | ios::trunc
(覆盖原有内容)。
cpp
#include <fstream>
#include <string>
int main() {
// 方式1:通过构造函数打开文件(默认覆盖模式)
std::ofstream ofs("output.txt"); // 若文件不存在则创建,存在则清空
if (!ofs.is_open()) { // 检查文件是否成功打开
// 打开失败(如权限不足、路径不存在)
return 1;
}
// 写入数据(支持 << 运算符,与 cout 用法一致)
ofs << "Hello, File!" << std::endl;
ofs << "This is a test." << std::endl;
int num = 123;
ofs << "Number: " << num << std::endl;
// 方式2:追加模式写入(需指定 ios::app)
std::ofstream ofs_app("output.txt", std::ios::app);
ofs_app << "This line is appended." << std::endl;
// 关闭文件
ofs.close();
ofs_app.close();
return 0;
}
运行后 output.txt
内容:
Hello, File!
This is a test.
Number: 123
This line is appended.
2. std::ifstream
:读取文件
用于从现有文件读取内容,默认模式是 ios::in
。
cpp
#include <fstream>
#include <iostream>
#include <string>
int main() {
// 打开文件(若文件不存在,打开失败)
std::ifstream ifs("output.txt");
if (!ifs.is_open()) {
std::cerr << "Failed to open file!" << std::endl;
return 1;
}
// 方法1:逐行读取(使用 getline)
std::string line;
std::cout << "----- 逐行读取 -----" << std::endl;
while (std::getline(ifs, line)) { // 读取到文件末尾时返回 false
std::cout << line << std::endl;
}
// 重置流状态(因为上面的循环已读到文件末尾,需清除 eof 标志)
ifs.clear();
// 定位到文件开头(重新读取)
ifs.seekg(0, std::ios::beg); // seekg:移动读指针(g = get)
// 方法2:逐个单词读取(使用 >> 运算符,忽略空格/换行)
std::cout << "\n----- 逐单词读取 -----" << std::endl;
std::string word;
while (ifs >> word) { // >> 自动跳过空白字符
std::cout << word << " | ";
}
ifs.close();
return 0;
}
运行输出:
----- 逐行读取 -----
Hello, File!
This is a test.
Number: 123
This line is appended.
----- 逐单词读取 -----
Hello, | File! | This | is | a | test. | Number: | 123 | This | line | is | appended. |
3. std::fstream
:双向读写文件
需显式指定打开模式(如 ios::in | ios::out
),支持同时读写。
cpp
#include <fstream>
#include <iostream>
#include <string>
int main() {
// 打开文件:可读可写,若不存在则创建
std::fstream fs("data.txt", std::ios::in | std::ios::out | std::ios::trunc);
if (!fs.is_open()) {
std::cerr << "Failed to open file!" << std::endl;
return 1;
}
// 写入数据
fs << "Apple\nBanana\nCherry" << std::endl;
// 定位到文件开头(准备读取)
fs.seekg(0, std::ios::beg);
// 读取并修改第2行(示例:将 "Banana" 改为 "Blueberry")
std::string line;
int line_num = 0;
std::string content; // 暂存所有内容
while (std::getline(fs, line)) {
line_num++;
if (line_num == 2) {
line = "Blueberry"; // 修改第2行
}
content += line + "\n";
}
// 定位到文件开头(准备重新写入修改后的内容)
fs.seekp(0, std::ios::beg); // seekp:移动写指针(p = put)
fs << content; // 写入修改后的内容
fs.close();
return 0;
}
运行后 data.txt
内容:
Apple
Blueberry
Cherry
4. 二进制文件操作
默认是文本模式(会对换行符等进行转换),指定 ios::binary
模式可读写二进制文件(如图片、音频)。读写时通常使用 read()
和 write()
函数。
cpp
#include <fstream>
#include <iostream>
// 示例:二进制写入并读取结构体
struct Student {
int id;
char name[20]; // 固定长度,避免字符串指针问题
float score;
};
int main() {
// 二进制写入
std::ofstream ofs("students.dat", std::ios::binary);
Student s1 = {1001, "Alice", 95.5f};
Student s2 = {1002, "Bob", 88.0f};
ofs.write((const char*)&s1, sizeof(Student)); // 写入字节流
ofs.write((const char*)&s2, sizeof(Student));
ofs.close();
// 二进制读取
std::ifstream ifs("students.dat", std::ios::binary);
Student s;
while (ifs.read((char*)&s, sizeof(Student))) { // 读取到缓冲区
std::cout << "ID: " << s.id
<< ", Name: " << s.name
<< ", Score: " << s.score << std::endl;
}
ifs.close();
return 0;
}
运行输出:
ID: 1001, Name: Alice, Score: 95.5
ID: 1002, Name: Bob, Score: 88
四、关键操作与技巧
-
检查文件状态:
is_open()
:判断文件是否成功打开。eof()
:判断是否到达文件末尾(读取时常用)。fail()
/bad()
:判断是否发生错误(如读写失败、格式错误)。clear()
:清除错误状态(如读取到末尾后重置,以便重新读写)。
-
文件指针定位:
- 读指针:
seekg(pos, mode)
(mode
可选ios::beg
开头、ios::cur
当前、ios::end
末尾)。 - 写指针:
seekp(pos, mode)
(双向流中读/写指针可能独立,需分别定位)。 - 获取当前指针位置:
tellg()
(读指针)、tellp()
(写指针)。
- 读指针:
-
避免中文乱码:文本模式下,文件编码需与程序输出编码一致(如均为 UTF-8 或 GBK),否则可能出现乱码。
五、注意事项
- 路径处理 :打开文件时可指定绝对路径(如
C:/data/file.txt
)或相对路径(相对于程序运行目录)。 - 资源释放 :即使忘记调用
close()
,流对象销毁时也会自动关闭文件,但手动关闭可避免资源泄漏(尤其在循环中频繁操作文件时)。 - 权限问题:若打开文件失败,需检查路径是否存在、是否有读写权限(如操作系统对某些目录的保护)。
文件流类是 C++ 处理文件 I/O 的核心工具,无论是文本文件(如日志、配置)还是二进制文件(如媒体、数据),都能通过它们灵活操作,是实际开发中(如数据持久化、文件解析)的基础技能。