C++<fstream> 深度解析:文件 I/O 全指南

<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
相关推荐
Aotman_2 小时前
Vue.directive:自定义指令及传参
前端·javascript·vue.js·elementui·ecmascript·es6
csbysj20202 小时前
API 类别 - 特效
开发语言
程序员码歌2 小时前
短思考第266天,玩IP路上的几点感悟,这几点很重要!
前端·后端·创业
行稳方能走远2 小时前
Android C++ 学习笔记
android·c++
梵得儿SHI2 小时前
2025 Vue 技术实战全景:从工程化到性能优化的 8 个落地突破
前端·javascript·vue.js·pinia2.2·响应式数据分片·展望vue3.6·2025年vue技术栈
运维行者_2 小时前
网络流量分析入门:从流量监控与 netflow 看懂核心作用
运维·开发语言·网络·云原生·容器·kubernetes·php
熊猫钓鱼>_>2 小时前
解决Web游戏Canvas内容在服务器部署时的显示问题
服务器·前端·游戏·canvas·cors·静态部署·资源路径
梦6502 小时前
React 封装 UEditor 富文本编辑器
前端·react.js·前端框架
Hao_Harrision2 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨ | DoubleClickHeart(双击爱心)
前端·typescript·react·tailwindcss·vite7