文件操作

文件操作基础概念

核心定义

对文件进行读(输入,从文件到程序)写(输出,从程序到文件) 的操作,称为文件操作。C++ 标准库提供了封装完善的接口类,简化文件交互流程。

核心头文件

头文件 作用
<fstream> 包含文件操作核心类(fstream/ifstream/ofstream
<ios> 包含流状态、打开模式等枚举定义
<istream> 输入流基础类(istream
<ostream> 输出流基础类(ostream
<streambuf> 流缓冲区管理类(streambuf
<iostream> 标准输入输出(cin/cout等,文件操作可复用流语法)
<sstream> 字符串流类(stringstream等,辅助文件数据处理)

核心接口类

C++ 文件操作的核心是三个流类,均继承自基础流类,接口统一:

类名 功能说明 构造函数原型
fstream 双向流(可读可写) fstream(const char *filename, openmode mode)
ifstream 输入流(仅读) ifstream(const char *filename, openmode mode)
ofstream 输出流(仅写) ofstream(const char *filename, openmode mode)

注:filename 为文件路径(相对路径/绝对路径),mode 为打开模式(见 1.4)。

文件打开模式

文件打开模式通过 ios 类的枚举值指定,支持组合使用(用 | 连接):

模式常量 含义 适用场景
ios::in 以输入模式打开(读文件) ifstream/fstream
ios::out 以输出模式打开(写文件),默认覆盖原有内容 ofstream/fstream
ios::app 追加模式(写文件时在末尾添加,不覆盖) ofstream/fstream
ios::binary 二进制模式(而非文本模式) 所有流类(读写二进制文件)
ios::trunc 若文件存在则清空内容(默认与 ios::out 绑定) ofstream/fstream
ios::ate 打开文件后定位到文件末尾 所有流类

常用组合:

  • 文本读:ios::in
  • 文本写(覆盖):ios::outios::out | ios::trunc
  • 文本写(追加):ios::out | ios::app
  • 二进制读:ios::in | ios::binary
  • 二进制写:ios::out | ios::binary
  • 读写兼顾:ios::in | ios::outfstream 专用)

核心类继承关系

C++ 文件操作核心类继承图谱
ios_base (抽象基类,定义流状态、格式控制)
    ↓
ios (继承 ios_base,封装流的基本接口)
    ↓
istream (输入流基类,cin 是其对象)
    ↓
ifstream (文件输入流)、istringstream(字符串输入流)
    ↑
iostream (继承 istream + ostream,双向流基类)
    ↓
ostream (输出流基类,cout/cerr/clog 是其对象)
    ↓
ofstream (文件输出流)、ostringstream(字符串输出流)
    ↑
fstream (文件双向流,继承 iostream)、stringstream(字符串双向流)

关键缓冲区概念

  • 文件操作默认使用缓冲区(由 streambuf 管理),数据不会立即写入文件/读取到程序,而是积累到一定大小后批量处理。
  • 手动刷新缓冲区:flush()(强制刷新)、endl(换行+刷新)。
  • 关闭文件时(close())会自动刷新缓冲区。

文件读取操作(输入:文件 → 程序)

头文件

#include <fstream>   // 核心头文件
#include <iostream>  // 用于 cout 输出读取结果
#include <cstring>   // 用于 strlen 等字符串操作(可选)
using namespace std; // 简化代码(实际项目可按需使用)

构造文件输入类(打开文件)

方法一:构造时直接打开(固定文件)

// 实例化对象时指定文件路径,默认以 ios::in 模式打开
ifstream ifs("./abc.txt"); 

方法二:先构造再打开(灵活切换文件)

ifstream ifs1; // 仅创建对象,未打开文件
ifs1.open("./even.cpp"); // 后续通过 open() 打开指定文件
// 可重复调用 open() 打开不同文件,但需先 close() 关闭当前文件

读取文件的常用方法

方法一:getline() 读取一行数据

char msg[128]; // 存储读取结果的缓冲区(128字节上限)
// 循环读取,直到文件末尾或出错
while (ifs1.good()) { 
    // 方式1:读取到换行符 '\n' 为止(默认分隔符)
    ifs1.getline(msg, 128); 
    // 方式2:自定义分隔符(读取到 ']' 为止,不包含分隔符)
    // ifs1.getline(msg, 128, ']'); 
    cout << msg << endl;
}

方法二:流提取符 >> 读取(类似 cin

char msg[128];
while (ifs.good()) {
    ifs >> msg; // 以空格/换行符为分隔符,自动跳过空白字符
    cout << msg << endl;
}

拓展:读取进阶技巧

先判断文件是否成功打开

ifstream ifs("hero.txt");
if (!ifs.is_open()) { // 或 if (!ifs),流对象可直接作为布尔值
    cerr << "文件打开失败!" << endl;
    return -1; // 终止程序或处理错误
}

读取二进制文件

// 打开模式需加 ios::binary
ifstream ifs("data.bin", ios::in | ios::binary);
if (!ifs) { cerr << "打开失败" << endl; return -1; }

// 读取指定字节数(例如读取一个 int 类型数据)
int num;
ifs.read((char*)&num, sizeof(int)); // 第一个参数是缓冲区地址(强制转换为 char*)
if (ifs.gcount() == sizeof(int)) { // gcount() 返回实际读取的字节数
    cout << "读取到:" << num << endl;
}

get()getline() 的区别

函数 处理换行符/分隔符 空白字符处理 适用场景
getline(buf, n) 丢弃分隔符(不存入buf) 读取整行(包括空格) 读取完整文本行
get(buf, n) 分隔符留在输入流中 读取到分隔符为止 逐段读取(需手动处理分隔符)

示例:get() 处理分隔符

char ch;
ifs.get(msg, 128, ','); // 读取到 ',' 为止
ifs.get(ch); // 读取并丢弃 ',' 分隔符

文件状态判断(替代 good()

成员函数 含义
eof() 是否到达文件末尾(End Of File)
fail() 操作失败(如读取类型不匹配、缓冲区溢出)
bad() 严重错误(如文件损坏、设备错误)
clear() 清除流状态标志(恢复操作能力)

推荐循环写法:

char msg[128];
while (ifs.getline(msg, 128)) { // getline() 成功返回 true,失败返回 false
    cout << msg << endl;
}
// 事后判断失败原因
if (ifs.eof()) {
    cout << "文件读取完毕" << endl;
} else if (ifs.fail()) {
    cout << "读取失败(缓冲区溢出或格式错误)" << endl;
}

文件写入操作(输出:程序 → 文件)

头文件

#include <fstream>
#include <iostream>
#include <cstring>
using namespace std;

构造文件输出类对象(打开文件)

ofstream ofs;
// 打开模式:ios::app(追加) + ios::out(输出),避免覆盖原有内容
ofs.open("abc.txt", ios::app | ios::out);

// 简化写法:构造时直接指定路径和模式
ofstream ofs2("abc2.txt", ios::out | ios::trunc); // 覆盖模式(默认)

写入文件的常用方法

方法一:write() 函数(二进制/文本通用)

const char* msg = "Hello GZ24合班";
// 第一个参数:数据地址,第二个参数:写入字节数
ofs.write(msg, strlen(msg)); 

方法二:流插入符 <<(类似 cout

const char* msg = "Hello GZ24合班";
ofs << msg << " eVEN" << endl; // endl 换行+刷新缓冲区

拓展:写入进阶技巧

二进制文件写入

ofstream ofs("data.bin", ios::out | ios::binary);
if (!ofs) { cerr << "打开失败" << endl; return -1; }

int score = 95;
// 写入 int 类型数据(需强制转换为 const char*)
ofs.write((const char*)&score, sizeof(int)); 

文件关闭的必要性

ofs.close(); // 关闭文件,自动刷新缓冲区,释放文件资源
// 关闭后若需重新写入,需再次调用 open()
  • 若未手动关闭,程序结束时会自动关闭,但可能导致缓冲区数据丢失(如异常退出时)。

打开模式组合注意事项

  • ios::app 会强制将写入位置定位到文件末尾,即使后续调用 seekp() 移动指针也无效。
  • ios::outios::trunc 绑定,若文件已存在,会先清空内容(除非加 ios::app)。
  • 读写兼顾时(fstream),需同时指定 ios::inios::out,否则可能无法正常读写。

缓冲区刷新

ofs << "内容1"; // 仅存入缓冲区,未写入文件
ofs.flush(); // 手动刷新缓冲区,确保数据写入文件
ofs << "内容2" << endl; // endl = 换行 + flush()

拓展示例

  1. 给英雄添加属性:技能(字符串数组)、攻击力(int)、防御力(int)、血量(int)等。
  2. 定义 Hero 类封装上述属性,提供构造函数、getter/setter 方法。
  3. 将【英雄名单.txt】(扩展属性后)的内容读取到 Hero 类对象中。
  4. 使用链表存储所有 Hero 对象,并实现遍历访问(输出每个英雄的完整信息)。

提示1:Hero 类示例

class Hero {
private:
    string name;       // 名字
    vector<string> skills; // 技能列表
    int attack;        // 攻击力
    int defense;       // 防御力
public:
    // 构造函数
    Hero(string n, vector<string> s, int a, int d) 
        : name(n), skills(s), attack(a), defense(d) {}
    // getter 方法
    string getName() const { return name; }
    void showInfo() const {
        cout << "英雄:" << name << endl;
        cout << "技能:";
        for (auto& skill : skills) cout << skill << " ";
        cout << "\n攻击力:" << attack << " 防御力:" << defense << endl;
    }
};

提示2:文件读取到链表示例

#include <list>
list<Hero> heroList; // 链表存储 Hero 对象

// 读取文件内容并创建 Hero 对象
ifstream ifs("英雄名单.txt");
if (!ifs) { cerr << "文件打开失败" << endl; return -1; }

string name, skill;
int attack, defense;
while (ifs >> name >> attack >> defense) { // 假设文件每行格式:名字 攻击力 防御力 技能1 技能2...
    vector<string> skills;
    while (ifs.peek() != '\n' && !ifs.eof()) { // 读取该行剩余技能
        ifs >> skill;
        skills.push_back(skill);
    }
    heroList.emplace_back(name, skills, attack, defense); // 构造对象并加入链表
}

// 迭代器遍历输出
for (list<Hero>::iterator it = heroList.begin(); it != heroList.end(); ++it) {
    it->showInfo();
    cout << "----------------" << endl;
}

posted @ 2025-12-31 08:54  Jaklin  阅读(1)  评论(0)    收藏  举报