文件IO操作

输入图片说明

1. 核心头文件与流对象

C++ 实现文件读写必须包含 <fstream> 头文件,该头文件提供了 3 个核心的文件流类,用于不同的文件操作场景:

流类名 功能说明 核心操作方向
ifstream 专门用于从文件读取数据 输入(读)
ofstream 专门用于向文件写入数据 输出(写)
fstream 既可读又可写文件 输入 + 输出

2. 文件打开模式

文件打开时需要指定模式,决定文件的操作规则,可通过 open() 函数或流对象构造函数指定,多个模式用 | 分隔:

模式常量 功能说明
ios::in 以只读方式打开文件;若文件不存在,打开失败
ios::out 以只写方式打开文件;若文件不存在则自动创建,若文件已存在则清空原有内容
ios::app 追加模式;写入的所有数据都会添加到文件末尾,不会清空原有内容
ios::trunc 截断模式;若文件已存在,打开时清空所有内容(ios::out 模式默认包含此特性)
ios::binary 二进制模式;按字节流读写(默认是文本模式,会处理换行符等特殊字符)
ios::ate 打开文件后,文件指针直接定位到文件末尾(可后续移动指针)

3. 文件路径规则

  • Windows 系统:路径中的反斜杠需要转义(如 C:\\test\\demo.txt),或直接使用正斜杠(C:/test/demo.txt);
  • Linux/Mac 系统:直接使用正斜杠(/home/user/demo.txt);
  • 相对路径:以程序运行目录为基准(如 ./demo.txt 表示当前目录,../demo.txt 表示上级目录)。

二、C++ 文件 IO 基础操作(文本文件)

1. 写入文件(ofstream)

核心流程:定义流对象 → 打开文件 → 检查是否打开成功 → 写入数据 → 关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
// 必须包含文件IO头文件
#include <fstream>
// 处理字符串需包含
#include <string>
using namespace std;

int main() {
// 1. 定义输出流对象
ofstream outFile;

// 2. 打开文件:参数1=文件路径,参数2=打开模式
outFile.open("demo.txt", ios::out);

// 3. 检查文件是否打开成功(必做!避免后续操作出错)
if (!outFile.is_open()) {
cout << "文件打开失败,请检查路径是否正确!" << endl;
// 异常退出,避免执行后续无效代码
return 1;
}

// 4. 写入数据:用法和cout一致,用<<运算符
outFile << "C++文件IO写入测试" << endl;
outFile << "整数:" << 100 << " 浮点数:" << 3.1415 << endl;
// 写入字符串变量
string str = "这是字符串内容";
outFile << str << endl;

// 5. 关闭文件:释放文件资源,必须执行
outFile.close();

return 0;
}

实际开发会将文件写入指定位置,而不是默认在同级目录下

1
2
3
4
5
string filename = "D:\\data\\txt\\test.txt";

ofstream fout;

fout.open(filename);

文件打开失败的主要原因:

  • 1.目录不存在
  • 2.磁盘空间已满
  • 3.没有权限

2. 读取文件(ifstream)

读取文件有 3 种常用方式,适配不同场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
// 方式1:构造函数直接打开文件(等价open())
ifstream inFile("demo.txt", ios::in);
if (!inFile) { // 简化写法:直接判断流对象状态,等价!inFile.is_open()
cout << "文件打开失败!" << endl;
return 1;
}

// ===== 读取方式1:逐字符读取 =====
cout << "【逐字符读取】:" << endl;
char ch;
// get(ch):读取一个字符到ch,读取成功返回true,到文件末尾返回false
while (inFile.get(ch)) {
cout << ch;
}
cout << endl;
// 重置文件指针(读完后指针在末尾,需重置才能重新读取)
inFile.clear(); // 清空流状态
inFile.seekg(0); // 指针移到文件开头

// ===== 读取方式2:按行读取(推荐处理文本文件) =====
cout << "【按行读取】:" << endl;
string line;
// getline(流对象, 字符串变量):读取一行内容到line,不包含换行符
while (getline(inFile, line)) {
cout << line << endl;
}
inFile.clear();
inFile.seekg(0);

// ===== 读取方式3:按单词读取(空格/换行分隔) =====
cout << "【按单词读取】:" << endl;
string word;
// >> 运算符:自动跳过空白符(空格、换行、制表符),读取到下一个空白符为止
while (inFile >> word) {
cout << word << " ";
}
cout << endl;

// 关闭文件
inFile.close();
return 0;
}

3. 追加写入文件(ios::app)

若不想清空原有文件,仅在末尾添加内容,使用 ios::app 模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <fstream>
using namespace std;

int main() {
// 追加模式打开文件
ofstream outFile("demo.txt", ios::app);
if (!outFile) {
cout << "文件打开失败!" << endl;
return 1;
}

// 写入的内容会添加到文件末尾
outFile << "这是追加的内容,不会覆盖原有内容" << endl;
outFile.close();

return 0;
}

三、二进制文件操作

二进制文件(如图片、视频、自定义结构体数据)不能用文本模式读写,需指定 ios::binary 模式,核心用 read()/write() 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <fstream>
using namespace std;

// 定义自定义结构体(示例二进制数据)
struct Student {
// 固定长度字符数组,避免指针问题
char name[20];
int age;
float score;
};

int main() {
// 1. 写入二进制文件
Student s1 = {"李明", 19, 88.9};
// 二进制写模式:ios::out | ios::binary
ofstream outFile("student.dat", ios::out | ios::binary);
if (!outFile) {
cout << "文件打开失败!" << endl;
return 1;
}
// write(数据起始地址, 数据字节数):按字节写入
// (char*)&s1:将结构体地址转为字符指针(字节级访问)
// sizeof(Student):获取结构体总字节数
outFile.write((char*)&s1, sizeof(Student));
outFile.close();

// 2. 读取二进制文件
Student s2;
ifstream inFile("student.dat", ios::in | ios::binary);
if (!inFile) {
cout << "文件打开失败!" << endl;
return 1;
}
// read(接收数据地址, 数据字节数):按字节读取
inFile.read((char*)&s2, sizeof(Student));
cout << "姓名:" << s2.name << endl;
cout << "年龄:" << s2.age << endl;
cout << "分数:" << s2.score << endl;
inFile.close();

return 0;
}

四、文件指针与随机访问

默认情况下,文件读写是顺序的,可通过文件指针实现随机读写:

  • seekg():调整输入流(读)的指针位置(g=get);

  • seekp():调整输出流(写)的指针位置(p=put);

  • 指针偏移量:从基准位置开始的字节数,基准位置有 3 种:

    • ios::beg:文件开头;
    • ios::cur:当前指针位置;
    • ios::end:文件末尾。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
// 读写模式打开文件
fstream file("demo.txt", ios::in | ios::out);
if (!file) {
cout << "文件打开失败!" << endl;
return 1;
}

// 1. 移动指针到文件开头偏移5字节的位置
file.seekg(5, ios::beg);
string content;
file >> content;
cout << "从第6字节开始的内容:" << content << endl;

// 2. 移动指针到文件末尾前10字节的位置
file.seekg(-10, ios::end);
file >> content;
cout << "文件末尾前10字节开始的内容:" << content << endl;

// 3. 获取当前指针位置(tellg()返回当前字节偏移量)
long pos = file.tellg();
cout << "当前指针位置:" << pos << " 字节" << endl;

file.close();
return 0;
}

五、常见注意事项

  1. 文件打开检查:无论哪种操作,必须先检查文件是否打开成功(is_open() 或直接判断流对象),否则后续操作会无意义且可能崩溃;
  2. 流状态管理:读取文件到末尾后,流会进入错误状态,需用 clear() 清空状态才能重新读取;
  3. 编码问题:C++ 无统一文件编码标准,Windows 系统默认 GBK,Linux/Mac 默认 UTF-8,读写中文时需保证编码一致;
  4. 大文件处理:避免一次性读取全部内容,优先用逐行 / 逐块读取(如 getline()),减少内存占用;
  5. 文件关闭:虽然流对象析构时会自动关闭文件,但显式调用 close() 是最佳实践,可及时释放文件资源。

总结

  1. C++ 文件 IO 核心依赖 <fstream> 头文件,通过 ifstream(读)、ofstream(写)、fstream(读写)实现文件操作,核心流程是定义流对象→打开文件→检查状态→操作数据→关闭文件
  2. 打开模式决定文件行为:ios::out 清空创建、ios::app 追加、ios::binary 处理二进制文件;
  3. 文本文件用 <</>>/getline() 读写,二进制文件用 read()/write()seekg()/seekp() 可实现文件指针的随机定位。