C++ 文件操作全知识点详细讲解

C++ 文件操作基于标准库 <fstream> ,以流(stream)为核心,提供面向对象的文件读写能力,比 C 语言的FILE*更安全、更灵活。本文从核心类、打开模式、基础操作、文本 / 二进制读写、指针操作、错误处理、高级功能最佳实践,全面覆盖所有关键知识点。

C++ 文件操作速查手册-CSDN博客https://blog.csdn.net/Howrun777/article/details/157380224?spm=1011.2415.3001.5331

一、核心头文件与类

1. 核心头文件

cpp 复制代码
#include <fstream>   // 核心:文件流类(ifstream/ofstream/fstream)
#include <iostream>  // 基础流(cin/cout),文件流继承自其基类
#include <string>    // 字符串处理(文件路径、读写内容)
#include <cstdio>    // 高级操作(重命名、删除文件,可选)

2. 三大核心文件流类

文件流类继承自标准 I/O 流基类,分工明确:

类名 继承关系 核心功能 适用场景
std::ifstream ios_baseiosistreamifstream 只读文件 读取配置文件、日志、文本内容
std::ofstream ios_baseiosostreamofstream 只写文件 写入日志、生成报告、保存数据
std::fstream ios_baseiosiostreamfstream 读写文件 同时需要读 / 写的场景(如修改文件)

关键:ifstream/ofstream 是单方向流,fstream 是双向流,可同时读写。


二、文件打开模式(核心)

打开文件时需指定打开模式std::ios_base::openmode 类型),决定文件的操作方式,可通过 | 组合多个模式。

1. 常用模式常量

模式常量 含义 适用类
std::ios::in 只读方式打开,文件不存在则打开失败 ifstream(默认)、fstream
std::ios::out 只写 方式打开,文件不存在则创建,存在则清空内容 (默认含trunc ofstream(默认)、fstream
std::ios::app 追加方式写,写入内容添加到文件末尾,不清空原有内容 ofstreamfstream
std::ios::trunc 打开时清空文件原有内容out 默认包含此模式) ofstreamfstream
std::ios::binary 二进制模式打开(默认是文本模式),不处理换行符等特殊字符 所有文件流类
std::ios::ate 打开文件后,文件指针直接定位到文件末尾(可后续移动指针) 所有文件流类
std::ios::nocreate 打开时不创建新文件,文件不存在则打开失败(部分编译器支持) 所有文件流类
std::ios::noreplace 打开时不覆盖已有文件,文件存在则打开失败(部分编译器支持) 所有文件流类

2. 各流默认模式

  • ifstream:默认 std::ios::in(只读)
  • ofstream:默认 std::ios::out | std::ios::trunc(只写 + 清空)
  • fstream:默认 std::ios::in | std::ios::out(读写,文件不存在则失败)

3. 模式组合示例

cpp 复制代码
// 1. 只读文本文件(ifstream默认,可省略模式)
std::ifstream in("dict.conf"); 

// 2. 只写+追加文本文件(不清空原有内容)
std::ofstream out("log.txt", std::ios::out | std::ios::app); 

// 3. 读写+二进制文件(用于图片、视频等)
std::fstream fs("data.bin", std::ios::in | std::ios::out | std::ios::binary); 

// 4. 只写+不创建新文件(文件不存在则失败)
std::ofstream out("exist.txt", std::ios::out | std::ios::nocreate); 
  • std::fstream in(_path); 中的 in 是 **std::fstream 类的实例对象 **,不是变量、指针或其他类型。
  • 这个对象封装了文件读取的所有状态和行为,是你操作文件的核心 "载体"。
  • 你对文件的所有读取操作(检查打开状态、逐行读取、移动指针等),本质上都是通过调用 in 这个对象的成员方法完成的。

三、文件操作基本流程

无论读 / 写,核心流程都是:打开文件 → 检查是否成功 → 读写操作 → 关闭文件

1. 打开文件(两种方式)

方式 1:构造函数直接打开(推荐,RAII 风格)

cpp 复制代码
// 只读打开
std::ifstream in("test.txt"); 
// 只写+追加打开
std::ofstream out("log.txt", std::ios::app); 
// 读写+二进制打开
std::fstream fs("data.bin", std::ios::in | std::ios::out | std::ios::binary); 

方式 2:先创建对象,再调用open()

cpp 复制代码
std::ifstream in;
in.open("test.txt", std::ios::in); // 显式指定模式

2. 检查文件是否打开成功(必须!)

文件打开可能失败(路径错误、权限不足、文件不存在等),必须先检查:

cpp 复制代码
std::ifstream in("test.txt");
if (!in.is_open()) { // 或直接写 if (!in),流对象可隐式转换为bool
    std::cerr << "文件打开失败!路径:" << "test.txt" << std::endl;
    return 1; // 异常退出
}

3. 关闭文件

  • 显式关闭 :调用close()方法,释放文件资源(推荐,尤其是长时间运行的程序)。
  • 自动关闭 :流对象析构时 会自动调用close()(RAII 特性,无需手动关也能释放资源)。
cpp 复制代码
in.close(); // 显式关闭
out.close();
fs.close();

四、文本文件读写(最常用)

文本文件以字符 为单位读写,默认会处理换行符 (Windows:\r\n,Linux:\n),适合配置文件、日志、纯文本内容。

1. 文本读操作(ifstream/fstream

(1)逐行读取(std::getline

最常用,适合读取配置文件、日志等按行存储的内容:

cpp 复制代码
#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::ifstream in("dict.conf");
    if (!in) {
        std::cerr << "打开失败" << std::endl;
        return 1;
    }

    std::string line;
    // 逐行读取,直到文件末尾(getline返回流对象,eof时为false)
    while (std::getline(in, line)) { 
        std::cout << "读取行:" << line << std::endl;
    }

    in.close();
    return 0;
}

(2)按空白符分割读取(>> 运算符)

>> 会自动跳过空格、换行、制表符等空白符,适合读取单词、数字:

cpp 复制代码
std::ifstream in("num.txt"); // 内容:10 20 30 40
int num;
while (in >> num) { // 每次读取一个整数
    std::cout << "读取数字:" << num << std::endl;
}
// 输出:10 20 30 40

(3)单字符读取(get()

读取单个字符(包括空白符、换行符),适合逐字符处理:

cpp 复制代码
std::ifstream in("test.txt");
char ch;
while (in.get(ch)) { // 读取单个字符
    std::cout << ch; // 原样输出所有字符(包括换行、空格)
}

2. 文本写操作(ofstream/fstream

(1)格式化写入(<< 运算符)

std::cout用法一致,支持格式化输出(如整数、字符串、浮点数):

cpp 复制代码
#include <iostream>
#include <fstream>
#include <string>

int main() {
    // 只写+追加打开,不清空原有内容
    std::ofstream out("log.txt", std::ios::app); 
    if (!out) {
        std::cerr << "打开失败" << std::endl;
        return 1;
    }

    // 写入字符串
    out << "程序启动时间:2026-01-26 10:00:00" << std::endl;
    // 写入整数+字符串
    out << "用户ID:" << 1001 << " | 操作:登录" << std::endl;
    // 写入浮点数
    out << "温度:" << 25.5 << "℃" << std::endl;

    out.close();
    return 0;
}

(2)单字符写入(put()

写入单个字符,适合逐字符输出:

cpp 复制代码
std::ofstream out("char.txt");
out.put('H');
out.put('e');
out.put('l');
out.put('l');
out.put('o');
out.put('\n'); // 写入换行符
// 文件内容:Hello

五、二进制文件读写

二进制文件以字节 为单位读写,不处理换行符等特殊字符 ,直接读写原始数据,适合图片、视频、可执行文件、自定义结构体等非文本数据

1. 核心要求

  • 打开时必须指定 std::ios::binary 模式。
  • 读写使用 read()write() 方法(而非>>/<<)。

2. 二进制读操作(read()

bash 复制代码
// 函数签名:istream& read(char* buffer, std::streamsize size);
// buffer:存储读取数据的缓冲区(char* 类型)
// size:要读取的字节数

示例:读取整数(4 字节)

cpp 复制代码
#include <iostream>
#include <fstream>

int main() {
    // 二进制只读打开
    std::ifstream in("data.bin", std::ios::in | std::ios::binary); 
    if (!in) {
        std::cerr << "打开失败" << std::endl;
        return 1;
    }

    int num;
    // 读取4字节(int的大小)到num中
    in.read(reinterpret_cast<char*>(&num), sizeof(num)); 
    std::cout << "读取整数:" << num << std::endl;

    in.close();
    return 0;
}

3. 二进制写操作(write()

bash 复制代码
// 函数签名:ostream& write(const char* buffer, std::streamsize size);
// buffer:要写入的数据缓冲区(const char* 类型)
// size:要写入的字节数

示例:写入整数 + 结构体

cpp 复制代码
#include <iostream>
#include <fstream>
#include <string>

// 自定义结构体
struct Student {
    int id;
    char name[20];
    float score;
};

int main() {
    // 二进制只写打开
    std::ofstream out("stu.bin", std::ios::out | std::ios::binary); 
    if (!out) {
        std::cerr << "打开失败" << std::endl;
        return 1;
    }

    // 1. 写入整数
    int num = 12345;
    out.write(reinterpret_cast<const char*>(&num), sizeof(num));

    // 2. 写入结构体
    Student stu = {1001, "张三", 95.5f};
    out.write(reinterpret_cast<const char*>(&stu), sizeof(stu));

    out.close();
    return 0;
}

注意:reinterpret_cast<char*> 是强制类型转换,将任意类型指针转为char*(字节指针),是二进制读写的核心技巧。


六、文件指针与随机访问

文件流内部维护读指针写指针 (文本模式下两者通常同步,二进制模式下可独立操作),通过指针可实现随机访问(直接跳转到文件任意位置读写)。

1. 核心指针操作函数

函数 所属类 功能 备注
seekg(pos) istream 移动读指针到绝对位置 g=get(读)
seekg(off, dir) istream 移动读指针到相对位置 off = 偏移量,dir = 基准位置
tellg() istream 获取当前读指针位置 返回字节数(std::streampos
seekp(pos) ostream 移动写指针到绝对位置 p=put(写)
seekp(off, dir) ostream 移动写指针到相对位置 同 seekg
tellp() ostream 获取当前写指针位置 同 tellg
cpp 复制代码
open: 打开指定路径的文件,可指定打开模式
//C 风格字符串版本
void open(const char* filename, std::ios_base::openmode mode = /* 类默认模式 */);
//ifstream 默认模式:std::ios::in
//ofstream 默认模式:std::ios::out | std::ios::trunc
//fstream 默认模式:std::ios::in | std::ios::out

//C++ string 版本(C++11+)
void open(const std::string& filename, std::ios_base::openmode mode = /* 类默认模式 */);
close: 关闭文件,释放文件资源
void close();
is_open: 检查文件是否成功打开,返回 bool(必须优先调用)
bool is_open() const;
eof: 检查是否到达文件末尾(end of file),返回 bool
bool eof() const;
fail: 检查是否发生非致命错误(如格式错误),返回 bool
bool fail() const;
bad: 检查是否发生致命错误(如文件损坏),返回 bool
bool bad() const;
clear: 清除流的错误状态(如 eof () 为 true 后,需调用 clear () 才能继续操作)
void clear(std::ios_base::iostate state = std::ios_base::goodbit);
//state:要设置的流状态,默认清除所有错误(设为goodbit)
seekg: 移动读指针到指定位置(g=get)
//版本 1(绝对位置)
std::istream& seekg(std::streampos pos);
//版本 2(相对位置)
std::istream& seekg(std::streamoff off, std::ios_base::seekdir dir);
//off:偏移字节数
//dir:基准位置(ios::beg/ios::cur/ios::end)
seekp: 移动写指针到指定位置(p=put)
//版本 1(绝对位置)
std::ostream& seekp(std::streampos pos);
//版本 2(相对位置)
std::ostream& seekp(std::streamoff off, std::ios_base::seekdir dir);
tellg: 返回当前读指针的位置(字节数)
std::streampos tellg() const;
tellp: 返回当前写指针的位置(字节数)
std::streampos tellp() const;

2. 基准位置(std::ios_base::seekdir

常量 含义
std::ios::beg 文件开头偏移
std::ios::cur 当前位置偏移
std::ios::end 文件末尾偏移

3. 随机访问示例

示例 1:读取文件末尾 10 字节

cpp 复制代码
#include <iostream>
#include <fstream>
#include <vector>

int main() {
    std::ifstream in("test.bin", std::ios::in | std::ios::binary);
    if (!in) return 1;

    // 1. 读指针移到文件末尾
    in.seekg(0, std::ios::end); 
    // 2. 获取文件总大小(末尾位置的字节数)
    std::streampos fileSize = in.tellg(); 
    // 3. 读指针移到「末尾-10字节」的位置
    in.seekg(fileSize - 10, std::ios::beg); 

    // 4. 读取10字节
    std::vector<char> buffer(10);
    in.read(buffer.data(), 10);

    // 输出读取的内容
    std::cout << "文件末尾10字节:";
    for (char ch : buffer) std::cout << ch;
    std::cout << std::endl;

    in.close();
    return 0;
}

示例 2:修改文件指定位置的内容

cpp 复制代码
#include <iostream>
#include <fstream>

int main() {
    // 读写+二进制打开
    std::fstream fs("data.bin", std::ios::in | std::ios::out | std::ios::binary); 
    if (!fs) return 1;

    // 1. 写指针移到第5字节位置(绝对位置)
    fs.seekp(5); 
    // 2. 写入新数据(覆盖原位置内容)
    int newNum = 999;
    fs.write(reinterpret_cast<const char*>(&newNum), sizeof(newNum));

    fs.close();
    return 0;
}

七、流状态与错误处理

文件流操作可能出现各种错误(如文件尾、格式错误、文件损坏),C++ 提供流状态标志状态检查函数,用于判断和处理错误。

1. 流状态标志(std::ios_base::iostate

标志位 含义
std::ios::goodbit 流状态正常,无错误
std::ios::eofbit 已到达文件末尾(EOF)
std::ios::failbit 发生非致命错误(如格式错误:读取整数时读到字母)
std::ios::badbit 发生致命错误(如文件损坏、磁盘故障、流被破坏)

2. 状态检查函数

函数 功能 返回值 等价判断(标志位)
good() 流状态是否正常 bool !eof() && !fail() && !bad()
eof() 是否到达文件末尾 bool (rdstate() & eofbit) != 0
fail() 是否发生非致命错误 bool `(rdstate() & (failbit badbit)) != 0`
bad() 是否发生致命错误 bool (rdstate() & badbit) != 0

3. 清除错误状态(clear()

当流出现错误(如eofbit/failbit)时,后续操作会失效,需调用clear()清除状态:

复制代码
// 函数签名:void clear(std::ios_base::iostate state = std::ios::goodbit);
// 默认清除所有错误,设为goodbit

示例:完整错误处理流程

cpp 复制代码
#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::ifstream in("test.txt");
    if (!in) {
        std::cerr << "打开失败" << std::endl;
        return 1;
    }

    std::string line;
    while (std::getline(in, line)) {
        // 正常处理每行内容
        std::cout << line << std::endl;
    }

    // 读取结束后,判断错误类型
    if (in.eof()) {
        std::cout << "文件读取完成(正常到达末尾)" << std::endl;
    } else if (in.fail()) {
        std::cerr << "读取格式错误(非致命)" << std::endl;
    } else if (in.bad()) {
        std::cerr << "文件读取出错(致命,如磁盘故障)" << std::endl;
    }

    // 清除错误状态(如需后续操作)
    in.clear();
    // 重新定位到文件开头(可选)
    in.seekg(0, std::ios::beg);

    in.close();
    return 0;
}

八、高级文件操作

1. 获取文件大小

通过读指针移到末尾 + tellg () 获取文件总字节数:

cpp 复制代码
std::ifstream in("test.bin", std::ios::binary);
if (!in) return 1;

in.seekg(0, std::ios::end); // 移到末尾
std::streampos fileSize = in.tellg(); // 获取大小(字节)
in.seekg(0, std::ios::beg); // 移回开头

std::cout << "文件大小:" << fileSize << " 字节" << std::endl;

2. 文件重命名与删除(<cstdio>

C++ 标准库未直接提供文件重命名 / 删除函数,需使用 C 标准库的std::rename()std::remove()

cpp 复制代码
#include <cstdio> // 必须包含

// 重命名文件:rename(旧路径, 新路径)
if (std::rename("old.txt", "new.txt") != 0) {
    std::cerr << "重命名失败" << std::endl;
}

// 删除文件:remove(文件路径)
if (std::remove("delete.txt") != 0) {
    std::cerr << "删除失败" << std::endl;
}

3. 临时文件

临时文件用于临时存储数据,程序结束后自动删除,有两种方式:

方式 1:std::tmpfile()(C 标准库,自动删除)

cpp 复制代码
#include <cstdio>

// 创建临时文件(二进制读写,程序结束自动删除)
FILE* tmpFile = std::tmpfile();
if (!tmpFile) {
    std::cerr << "创建临时文件失败" << std::endl;
    return 1;
}

// 写入临时文件
std::fputs("临时数据", tmpFile);
// 读取临时文件
std::rewind(tmpFile); // 移到文件开头
char buffer[100];
std::fgets(buffer, 100, tmpFile);
std::cout << "临时文件内容:" << buffer << std::endl;

// 无需手动关闭,程序结束自动删除

方式 2:fstream创建临时文件(手动管理)

cpp 复制代码
std::string tmpPath = "tmp_" + std::to_string(rand()) + ".tmp";
std::ofstream tmpOut(tmpPath);
if (tmpOut) {
    tmpOut << "临时数据" << std::endl;
    tmpOut.close();

    // 使用临时文件...

    // 手动删除
    std::remove(tmpPath.c_str());
}

九、注意事项与最佳实践

1. 路径问题

  • 相对路径 :相对于程序运行目录 (而非代码文件目录),如./dict.conf(当前目录)、../conf/dict.conf(上级目录)。
  • 绝对路径 :如C:/test/dict.conf(Windows)、/home/test/dict.conf(Linux),稳定性高但移植性差。
  • 跨平台路径分隔符 :优先使用/(Windows 和 Linux 均支持),避免\(Windows 专属,需转义为\\)。

2. 资源管理(RAII)

  • 优先使用构造函数打开文件 ,流对象析构时自动关闭,无需手动调用close()(但显式调用更规范)。
  • 避免手动管理FILE*,优先使用 C++ 文件流(更安全,无内存泄漏风险)。

3. 权限问题

  • 写文件时,程序需拥有目标目录的写入权限 ;读文件时,需拥有文件的读取权限
  • Windows 下需避免写入系统目录(如C:\Windows),Linux 下需避免写入/root等特权目录。

4. 编码问题

  • 文本文件默认编码:Windows 为 ANSI(GBK),Linux 为 UTF-8,跨平台需统一为UTF-8
  • 读写 UTF-8 文件时,无需特殊处理(C++ 流默认支持 UTF-8),但需注意中文在不同编码下的字节数差异。

5. 性能优化

  • 大文件读写:优先使用二进制模式 + 缓冲区 (如std::vector<char>),减少 IO 次数。
  • 避免频繁打开 / 关闭文件:长时间操作的文件,打开一次即可,用完再关闭。

6. 错误处理

  • 必须检查is_open(),否则后续操作会导致程序崩溃。
  • 读取后检查流状态,区分eof(正常结束)和fail/bad(错误结束)。
  • 清除错误状态后,需重新定位文件指针(如seekg(0))才能继续操作。

7. C++11+ 新特性

  • std::string 版本的open() :支持直接传入std::string路径(无需转const char*)。
  • 移动语义:文件流支持移动构造 / 赋值,可高效传递流对象(避免拷贝)。
  • std::filesystem (C++17):提供更强大的文件系统操作(如遍历目录、获取文件属性),替代部分<cstdio>功能。

十、总结

C++ 文件操作的核心是流对象 + 打开模式 + 读写方法,核心逻辑可概括为:

  1. 选对类:ifstream(读)、ofstream(写)、fstream(读写)。
  2. 选对模式:根据需求组合in/out/app/binary等。
  3. 规范流程:打开→检查→读写→关闭,做好错误处理。
  4. 区分场景:文本文件用>>/getline/<<,二进制文件用read/write+binary模式。
  5. 优化性能:大文件用二进制 + 缓冲区,避免频繁 IO。

掌握以上知识点,即可应对 C++ 中所有文件操作场景(配置读取、日志写入、数据存储、二进制处理等)。

常用场景示例

1. 逐行读取文本文件

cpp 复制代码
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    ifstream in("config.txt");
    if (!in) { cerr << "打开失败"; return 1; }
    
    string line;
    while (getline(in, line)) {
        cout << "读取行:" << line << endl;
    }
    in.close();
    return 0;
}

2. 二进制读写结构体

cpp 复制代码
#include <fstream>
#include <iostream>
using namespace std;

struct Student { int id; char name[20]; float score; };

int main() {
    // 写入
    ofstream out("stu.bin", ios::out | ios::binary);
    Student s = {1001, "张三", 95.5};
    out.write((char*)&s, sizeof(s));
    out.close();

    // 读取
    ifstream in("stu.bin", ios::in | ios::binary);
    Student s_read;
    in.read((char*)&s_read, sizeof(s_read));
    cout << "ID:" << s_read.id << " 姓名:" << s_read.name << " 分数:" << s_read.score << endl;
    in.close();
    return 0;
}
相关推荐
m0_748248654 小时前
C++使用HTTP库和框架轻松发送HTTP请求
开发语言·c++·http
朔北之忘 Clancy4 小时前
2025 年 12 月青少年软编等考 C 语言二级真题解析
c语言·开发语言·c++·学习·算法·青少年编程·题解
2301_790300964 小时前
C++与增强现实开发
开发语言·c++·算法
zmzb01034 小时前
C++课后习题训练记录Day82
开发语言·c++
Howrun7774 小时前
C++ 文件操作速查手册
c++
C++ 老炮儿的技术栈5 小时前
不调用C++/C的字符串库函数,编写函数strcmp
c语言·开发语言·c++·人工智能·windows·git·visual studio
茉莉玫瑰花茶5 小时前
脚手架介绍
c++
Hgfdsaqwr5 小时前
内存泄漏检测与防范
开发语言·c++·算法
茉莉玫瑰花茶5 小时前
C++17 详细特性解析(中)
开发语言·c++