25、文件操作:文件流(fstream, ifstream, ofstream)、文本文件读写、二进制文件读写、文件定位
文件操作,说白了就是让程序能跟硬盘上的数据打交道。你写的代码跑在内存里,一断电就全没了。但文件不一样,它是持久化的。我刚开始学C++时,觉得文件操作就是「读进来、写出去」,没啥技术含量。直到有一次我写一个日志系统,因为没处理好二进制和文本模式的区别,导致跨平台时数据全乱了……嗯,从那以后我再也不敢小看文件操作了。
25.1 文件流的三兄弟:fstream、ifstream、ofstream
C++ 标准库提供了三个核心类来处理文件:
- ifstream:输入文件流,专门用来读文件。
- ofstream:输出文件流,专门用来写文件。
- fstream:文件流,既能读也能写。
我个人习惯是:如果只需要读,就用 ifstream;只需要写,就用 ofstream;需要同时读写,才用 fstream。这样代码意图更清晰,别人一看就知道你要干嘛。
核心要点:文件流对象在生命周期结束时,会自动调用析构函数关闭文件。但为了保险,我建议你手动调用 .close(),尤其是在长时间运行的程序里。
25.2 文本文件读写
文本文件,就是你能用记事本打开、直接看到内容的文件。读写方式很简单,用 << 和 >> 操作符就行。
写文本文件
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream outFile("example.txt");
if (!outFile.is_open()) {
cerr << "文件打开失败!" << endl;
return 1;
}
outFile << "姓名:张三" << endl;
outFile << "年龄:25" << endl;
outFile << "分数:92.5" << endl;
outFile.close();
return 0;
}
读文本文件
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ifstream inFile("example.txt");
if (!inFile.is_open()) {
cerr << "文件打开失败!" << endl;
return 1;
}
string line;
while (getline(inFile, line)) {
cout << line << endl;
}
inFile.close();
return 0;
}
小技巧:用 getline() 读取整行,比用 >> 更安全。>> 遇到空格就停了,容易丢数据。我在项目中遇到过用 >> 读配置文件,结果字段里有空格,解析全乱套了。
25.3 二进制文件读写
二进制文件,说白了就是按字节读写,不关心内容是什么。图片、视频、可执行文件都是二进制格式。读写时用 read() 和 write() 成员函数。
写二进制文件
#include <iostream>
#include <fstream>
using namespace std;
struct Student {
char name[32];
int age;
double score;
};
int main() {
Student stu = {"李四", 22, 88.5};
ofstream outFile("student.dat", ios::binary);
if (!outFile.is_open()) {
cerr << "文件打开失败!" << endl;
return 1;
}
outFile.write(reinterpret_cast<char*>(&stu), sizeof(stu));
outFile.close();
return 0;
}
读二进制文件
#include <iostream>
#include <fstream>
using namespace std;
struct Student {
char name[32];
int age;
double score;
};
int main() {
Student stu;
ifstream inFile("student.dat", ios::binary);
if (!inFile.is_open()) {
cerr << "文件打开失败!" << endl;
return 1;
}
inFile.read(reinterpret_cast<char*>(&stu), sizeof(stu));
cout << "姓名:" << stu.name << endl;
cout << "年龄:" << stu.age << endl;
cout << "分数:" << stu.score << endl;
inFile.close();
return 0;
}
警告:二进制文件读写时,一定要用 ios::binary 模式打开。否则在 Windows 系统下,换行符 \n 会被自动转换成 \r\n,导致数据错位。我曾经因为这个 bug 排查了整整一个下午……
25.4 文件定位
文件定位,就是控制「当前读写位置」在文件中的哪里。C++ 提供了两个关键函数:
tellg()/tellp():获取当前读/写位置(返回streampos类型)。seekg()/seekp():设置读/写位置。
你想想看,为什么要有定位?因为有时候你不想从头读到尾,比如你想直接跳到文件第100个字节开始读。这时候定位就派上用场了。
seekg 的三种定位方式
| 定位方式 | 说明 | 示例 |
|---|---|---|
ios::beg |
从文件开头算起 | seekg(10, ios::beg) → 跳到第10字节 |
ios::cur |
从当前位置算起 | seekg(5, ios::cur) → 向后跳5字节 |
ios::end |
从文件末尾算起 | seekg(-10, ios::end) → 跳到倒数第10字节 |
示例:读取文件最后20个字节
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream inFile("example.txt", ios::binary);
if (!inFile.is_open()) {
cerr << "文件打开失败!" << endl;
return 1;
}
// 跳到文件末尾前20字节
inFile.seekg(-20, ios::end);
char buffer[21] = {0};
inFile.read(buffer, 20);
cout << "最后20字节:" << buffer << endl;
inFile.close();
return 0;
}
避坑指南:我曾经在读取大文件时,忘了检查 seekg() 是否成功。结果文件比预期小,定位到负数位置,读出来的数据全是乱码。所以记得用 if (!inFile) { /* 处理错误 */ } 来检查定位是否成功。
25.5 知识体系总览
下面这张图,帮你把本章的知识点串起来:
这张图把文件操作分成了三大块:文本文件、二进制文件、文件定位。每一块都有对应的类和函数。你写代码时,先想清楚你要处理什么类型的文件,再选对应的工具。别一上来就 fstream 一把梭,那样容易出问题。
总结一下:文件操作是C++里非常实用的技能。文本文件适合人类可读的数据,二进制文件适合程序内部数据。文件定位让你能随机访问文件内容,而不是只能从头读到尾。记住:打开文件后一定要检查是否成功,读写完毕后记得关闭文件。这些习惯能帮你省下不少调试时间。