<fstream> 是 C++ 标准库中处理文件输入输出 的核心头文件,它继承并扩展了 <iostream> 的流操作体系,提供了面向文件的流类(ifstream/ofstream/fstream),支持文本文件和二进制文件的读写,是文件操作的基础。
1. 核心类与继承关系
<fstream> 定义了三个核心类,均继承自 <istream>/<ostream> 的基类,关系如下:
cpp
继承的流类 专用的缓冲区类 功能
ifstream (继承 istream) filebuf (继承 streambuf) 专用于文件的输入操作。
ofstream (继承 ostream) 专用于文件的输出操作。
fstream (继承 iostream) 专用于文件的输入和输出操作。
底层依赖:fstream 类最终通过 filebuf(<streambuf> 的派生类)管理文件缓冲区,实现数据与文件的交互。
2. 文件打开模式(Open Mode)
ios::in :以只读模式打开文件,文件必须存在。适用类:ifstream/fstream,ofstream 默认不包含此模式。
ios::out :以只写模式打开文件,文件不存在则创建,存在则截断(清空) 原有内容。适用类: ofstream/fstream,ifstream 默认不包含此模式。
ios::app :追加模式,写入数据时追加到文件末尾(而非覆盖)。适用类: ofstream/fstream 需配合 ios::out 使用。
ios::trunc :截断模式,打开文件时清空原有内容(ios::out 默认包含此模式)。适用类: ofstream/fstream,与 ios::app 互斥。
ios::binary :二进制模式,禁用文本模式的换行符转换(\n↔\r\n)。适用类:所有,读写二进制文件(如图片、视频)必须指定。
ios::ate:打开文件后,将文件指针定位到文件末尾(可移动指针)。适用类:所有,区别于 ios::app(仅追加,指针不可移动)。
ios::nocreate:(部分编译器支持)打开文件时,若文件不存在则失败。适用类:ifstream,非标准,但常见于编译器扩展。
ios::noreplace:(部分编译器支持)打开文件时,若文件存在则失败 。适用类:ofstream,非标准,避免覆盖已有文件。
模式组合示例:
cpp
ios::out | ios::app:追加写入文件(文件不存在则创建);
ios::in | ios::out:可读可写,文件必须存在(不截断);
ios::in | ios::out | ios::trunc:可读可写,文件不存在则创建,存在则清空;
ios::in | ios::binary:二进制模式读取文件。
3. 文件流的核心操作
3.1. 打开文件:构造函数 / open() 方法
文件流有两种打开方式:
-
构造函数打开:创建流对象时直接指定文件名和模式;
-
open() 方法打开:先创建空对象,后续调用 open() 打开文件。
cpp
// 构造函数
ifstream ifs(const string& filename, ios::openmode mode = ios::in);
ofstream ofs(const string& filename, ios::openmode mode = ios::out);
fstream fs(const string& filename, ios::openmode mode = ios::in | ios::out);
// open() 方法
void open(const string& filename, ios::openmode mode = ...);
fstream类构造函数:
cpp
default (1)
fstream();
initialization (2)
explicit fstream (const char* filename,
ios_base::openmode mode = ios_base::in | ios_base::out);
explicit fstream (const string& filename,
ios_base::openmode mode = ios_base::in | ios_base::out);
copy (3)
fstream (const fstream&) = delete;
move (4)
fstream (fstream&& x);
示例:
cpp
#include <iostream>
#include <fstream>
using namespace std;
void OpenFile()
{
// 方式1:构造函数打开(ofstream 默认 ios::out)
ofstream outfile1("file1.txt");
// 方式2:open() 方法打开(追加模式)
ofstream outfile2;
outfile2.open("file2.txt", ios::out | ios::app);
// 方式3:fstream 可读可写(不截断)
fstream fs("file3.txt", ios::in | ios::out);
if(!fs.is_open()) // 检查是否打开成功(关键!)
{
cout << "file3.txt文件打开失败" << endl;
return;
}
outfile1.close();
outfile2.close();
fs.close();
return;
}
3.2. 检查文件状态:核心判断函数
文件操作的错误处理是重中之重,需通过以下函数检查状态:
cpp
bool is_open():文件是否成功打开(最常用,优先检查)
bool good():流状态正常(无错误、无 EOF)
bool fail():逻辑错误(如打开不存在的文件、读取格式错误)
bool bad():严重错误(如硬件故障、缓冲区损坏)
bool eof():是否到达文件末尾(End of File)
operator!():等价于 !good(),可直接 if (!fs) { ... }
cpp
void CheckFileStatu()
{
ifstream ifs("hello.txt");
if (!ifs.is_open())
{
cerr << "open hello.txt error";
return;
}
// 读取过程中检查状态
int num;
while (ifs >> num) // 读取成功则继续(等价于 !inFile.fail())
std::cout << num << " ";
// 分析读取终止原因
if (ifs.eof())
cout << "\nInfo: Reach end of file\n";
else if (ifs.fail())
{
cerr << "\nError: Read failed (invalid data format)\n";
ifs.clear(); // 清除错误状态(可选)
}
else if (ifs.bad())
cerr << "\nFatal error: Stream corrupted\n";
ifs.close();
return;
}
3.3. 文本文件读写
文本文件以字符流形式处理,支持 >>/<< 运算符、getline()、get()/put() 等操作。
(1)写入文本文件
核心:<< 运算符(与 cout 用法一致),支持所有基本类型(int、double、string 等);
追加写入:需指定 ios::app 模式。
cpp
void TextWriting()
{
ofstream out("hello.txt");
if(!out)
{
cerr << "open hello.txt failed";
return;
}
out << "Name:" << "wcy" << "\n";
out << "Age: " << 20 << "\n";
out << "Weight: " << 130 << endl;
out.close();
ofstream app("hello.txt", ios::app);
app << "this is append info" << endl;
return;
}
(2)读取文本文件
按分隔符读取:>> 运算符(默认以空格 / 换行 / 制表符为分隔符);
按行读取:getline(stream, str)(读取整行,包括空格,直到换行符);
单个字符读取:get()(读取单个字符,包括空白符)。
cpp
// 文本读取
void TextReading()
{
ifstream in("hello.txt");
if(!in.is_open())
{
cerr << "open hello.txt failed";
return;
}
// 方式1:按分隔符读取(空格、\n、\t等)
std::string key, value;
while(in >> key >> value)
{
cout << key << " " << value << endl;;
}
in.clear(); // 重置流状态(因为上面读取到EOF,需清除)
in.seekg(0); // 将读取指针移回文件开头
// 方式2:按行读取
std::string line;
while(getline(in, line))
{
cout << line << endl;
}
in.clear();
in.seekg(0);
// 方式3:单个字符读取
char c;
while(in.get(c))
cout << c;
in.close();
return;
}
3.4. 二进制文件读写
二进制文件以字节流形式处理,不进行字符转换(如 \n 不会转为 \r\n),需指定 ios::binary 模式,核心用 read()/write() 方法。
cpp
读取二进制数据:从流中读取 n 字节到 buf 指向的内存
istream& read(char* buf, streamsize n);
写入二进制数据:将 buf 指向的 n 字节写入流
ostream& write(const char* buf, streamsize n);
cpp
void BinaryFileReadWrite()
{
ofstream out("hello.txt", ios::binary); // ofstream默认ios::out
if (!out)
return;
std::string str = "write as binary";
out.write(str.c_str(), str.size());
out.close();
ifstream in("hello.txt", ios::binary);
if (!in)
return;
in.seekg(0, ios::end); // 移动读指针到文件末尾
size_t fileSize = in.tellg(); // 返回当前"读"指针相对于文件开头的偏移量,这个值就是文件的大小。
in.seekg(0, ios::beg); // 移动读指针回文件开头 (准备读取)
vector<char> buf(fileSize);
in.read(buf.data(), fileSize);
// 检查读取是否成功,gcount() 返回实际读取的字节数
if (in.gcount() == fileSize)
{
string str(buf.begin(), buf.end());
cout << str << endl;
}
in.close();
return;
}
3.5. 文件指针定位:seekg() / seekp() / tellg() / tellp()
文件流有两个指针:
读取指针(get pointer):指向下次读取的位置(ifstream/fstream);
写入指针(put pointer):指向下次写入的位置(ofstream/fstream)。
cpp
seekg(pos):将读取指针移到绝对位置 pos(字节数,从文件开头算)
seekg(offset, dir):将读取指针相对 dir 移动 offset 字节:
dir:ios::beg(开头)、ios::cur(当前)、ios::end(末尾)
tellg():返回读取指针的当前位置(字节数)
seekp(pos):移动写入指针(用法同 seekg)
seekp(offset, dir):将读取指针相对 dir 移动 offset 字节:
dir:ios::beg(开头)、ios::cur(当前)、ios::end(末尾)
tellp():返回写入指针的当前位置
cpp
void FilePointerPos()
{
// 以可读可写模式打开,修改第5个字符
fstream fs("hello.txt", ios::in | ios::out);
if (!fs) return;
fs << "0123456789";
// 移动写入指针到第5个字符(从开头偏移4字节)
fs.seekp(4, ios::beg);
fs.put('X'); // 将第5个字符改为 'X'
// 移回开头,读取全部内容
fs.seekg(0, ios::beg);
char buf[11];
fs.read(buf, 10);
buf[10] = 0;
cout << "Modified content: " << buf << endl; // 输出 0123X56789
// 获取指针当前位置
cout << "Current position: " << fs.tellg() << endl; // 输出 10(EOF)
cout << "Current position: " << fs.tellp() << endl; // 输出 10(EOF)
fs.close();
return;
}
4. 注意事项
(1)关闭文件的注意事项
-
文件流析构时会自动关闭文件,但显式调用 close() 更规范(尤其是多次复用流对象时);
-
关闭文件时,缓冲区会自动刷新(未写入的数据会被写入文件);
-
关闭后可重新调用 open() 打开其他文件。
-
一个文件在未关闭前,能否被其他模式继续打开,取决于操作系统和文件共享模式的设置。
(2)大文件处理:缓冲区优化
-
文本模式下,endl 会强制刷新缓冲区,频繁使用会降低性能,建议用 **'\n'**替代,仅在必要时调用 flush();
-
二进制模式下,read()/write() 可指定大块字节数(如 4096 字节),减少系统调用次数。
(3)跨平台注意事项
-
文本模式下,Windows 会将 \n 转换为 \r\n,Linux/Mac 无转换;二进制模式需显式指定 ios::binary,避免换行符转换;
-
文件路径分隔符:Windows 用 \(需转义为 \\),Linux/Mac 用 /,建议统一用 /(C++ 跨平台支持)。
5. 常见错误与排查
cpp
is_open() 返回 false :
1. 文件路径错误;2. 权限不足;3. 文件被占用
解决方案:1. 检查路径(用绝对路径);2. 确认权限;3. 关闭占用程序
读取内容为空:1. 文件指针在 EOF;2. 模式错误(如只读模式写入)
解决方案:1. 用 seekg() 移回开头;2. 检查打开模式
二进制读取数据错乱:1. 未指定 ios::binary;2. 结构体内存对齐
解决方案:1. 强制指定 ios::binary;2. 用固定大小类型
追加模式写入不生效:未配合 ios::out 模式
解决方案:组合 ios::out