C++文件读写(fstream)——像写日记一样读写文件
较难19用 fstream 读写文件——像管理日记本一样轻松存取数据
你有没有想过,程序运行完之后,数据怎么保存下来呢?比如游戏中的最高分、每天记的账本、班级的考试成绩……这些数据不能只存在内存里,因为关掉程序就没了。这时候就需要文件来帮忙。C++ 里的 fstream 就像一本可以反复翻看的日记本——你可以翻开它(打开文件),在上面写字(写入数据),或者读它以前写的内容(读取数据)。学会了它,你的程序就能把数据“记住”啦!
准备工作:认识三个“文件专家”
要使用 fstream,先要包含头文件 <fstream>。它带来了三个好兄弟:
ifstream(in file stream):专门用来读文件,就像你打开日记本只看不写。ofstream(out file stream):专门用来写文件,就像你在日记本上写字(但可能会擦掉旧字)。fstream:既能读又能写,最全能,但使用时要注意模式。
基本四步走:打开 → 读写 → 关闭
不管哪个类,使用流程都一样:
- 创建对象:比如
ifstream fin;(创建了一个读文件对象)。 - 打开文件:用
open(文件名, 模式),比如fin.open("data.txt");。 - 读写数据:像用
cin一样读(fin >> 变量),像用cout一样写(fout << 数据)。 - 关闭文件:
fin.close();,就像合上日记本,别让它一直敞着。
例子1:读文件中的两个数并求和
假设 numbers.txt 文件里有 10 和 20(用空格或换行隔开),下面程序读出来算和:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream fin;
fin.open("numbers.txt"); // 打开日记本
int a, b; // 两个数字
fin >> a >> b; // 从文件里读,就像从键盘输入一样
fin.close(); // 合上日记本
cout << "和是:" << a + b << endl;
return 0;
}
例子2:写一句话到文件
#include <fstream>
using namespace std;
int main() {
ofstream fout;
fout.open("hello.txt"); // 新建或打开日记本(如果不存在会创建)
fout << "Hello, GESP!"; // 往日记本里写一行字
fout.close(); // 合上日记本
return 0;
}
打开模式:控制“日记本”的读写行为
打开文件时,第二个参数可以指定模式,告诉计算机你想怎么用这本日记本。常用模式有:
| 模式 | 含义 | 生活类比 |
|---|---|---|
ios::in | 读模式(ifstream 默认自带) | 只翻开看,不动笔 |
ios::out | 写模式(ofstream 默认自带) | 翻开写字,但会擦掉旧页的内容 |
ios::app | 追加模式,写在末尾,不擦旧内容 | 在日记本末尾接着写 |
ios::ate | 打开后直接定位到文件末尾 | 一翻开就跳到最后一页 |
ios::trunc | 如果文件存在,清空它 | 把整本日记撕掉,重新写 |
多个模式可以组合,用 | 连接。例如:
fstream fstr;
fstr.open("data.txt", ios::in | ios::out); // 既能读又能写
生活小例子:
- 你想记录每天的零花钱收入,用
ios::app追加,这样不会把昨天的记录弄丢。 - 你要重新制定考试目标,用
ios::out覆盖旧文件,只留最新计划。
每次打开都要“体检”:检查是否成功
就像拿到一本日记本要先看看它有没有缺页,打开文件后一定要检查是否成功了。如果文件名写错、文件路径不对或者磁盘满了,打开就会失败。如果不检查,程序可能会乱读乱写,甚至崩溃。
检查的方法有两种:
if (!fin) { // 如果 fin 是“假”(打开失败)
cout << "文件打开失败!" << endl;
return 1; // 退出程序,返回非0表示出错
}
或者:
if (!fin.is_open()) {
cout << "文件打开失败!" << endl;
return 1;
}
新手最容易忘的就是这一步,结果程序跑到一半发现读不到数据,闹出“灵异”bug。
新手容易犯的几个错误
-
忘记关闭文件
打开后不close(),程序运行期间文件一直被占用,其他程序可能无法读写。如果写文件,数据可能还留在缓冲区没真正存到磁盘。解决办法:成对使用open和close,可以用“打开→处理→关闭”的思维。 -
路径写错
"data.txt"默认在程序所在的文件夹(当前目录)。如果文件在别的文件夹,要写完整路径,如"C:\\myfolder\\data.txt"(Windows 用双反斜杠)。最好把文件放在项目文件夹下。 -
模式用错
用ofstream打开一个已有文件,默认会清空它(ios::out带ios::trunc效果)。如果想保留旧内容只追加,要用ios::app。
例如:ofstream fout; fout.open("score.txt", ios::app); // 追加在末尾 -
读不到数据就乱用变量
如果文件是空的,或者已经读到末尾,再fin >> x会失败,变量保持原来的值(或未定义)。可以用while (fin >> x)循环读取直到结束。 -
混合读写顺序混乱
用fstream同时读写时,注意文件指针的位置。写完想读,可能需要先seekg或关闭重开。简单起见,一般情况下只用ifstream或ofstream一种功能,避免混淆。
完整示例:读取全班考试成绩并计算平均分
假设有一个 scores.txt 文件,内容如下(每行一个同学的成绩,可能有多个):
90
85
72
98
我们要读取所有成绩,计算平均分并输出,同时把每位同学的成绩单独写入一个新文件 report.txt。
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream fin;
fin.open("scores.txt"); // 打开成绩文件
if (!fin) { // 检查是否打开成功
cout << "无法打开 scores.txt!" << endl;
return 1;
}
int score; // 存放每个成绩
int sum = 0; // 总分
int count = 0; // 人数
while (fin >> score) { // 循环读取,直到文件末尾
sum += score; // 累加总分
count++; // 人数加1
cout << "读取到成绩:" << score << endl;
}
fin.close(); // 读完关闭输入文件
if (count == 0) {
cout << "文件为空,没有成绩。" << endl;
return 1;
}
double average = (double)sum / count; // 平均分(注意转成浮点数)
cout << "总分:" << sum << ",人数:" << count << endl;
cout << "平均分:" << average << endl;
// 把平均分写入 report.txt(覆盖方式)
ofstream fout;
fout.open("report.txt"); // 创建或覆盖报告文件
if (!fout) {
cout << "无法创建 report.txt!" << endl;
return 1;
}
fout << "全班成绩统计" << endl;
fout << "人数:" << count << endl;
fout << "总分:" << sum << endl;
fout << "平均分:" << average << endl;
fout.close(); // 关闭输出文件
cout << "统计信息已写入 report.txt" << endl;
return 0;
}
运行结果(假设 scores.txt 内容如上):
读取到成绩:90
读取到成绩:85
读取到成绩:72
读取到成绩:98
总分:345,人数:4
平均分:86.25
统计信息已写入 report.txt
拓展一步:还能做什么?
- 读取整行文本:用
getline(fin, 字符串)可以读取包含空格的整行内容,比如读取姓名、地址。 - 二进制文件:
fstream还可以读写图片、音频等二进制文件,需要设置ios::binary模式。 - 文件指针控制:用
tellg()/tellp()知道当前读写位置,用seekg()/seekp()跳到指定位置,实现随机访问。 - 同时处理多个文件:可以创建多个
ifstream或ofstream对象,同时打开几个文件。
学会了 fstream,你就掌握了 C++ 程序“存下记忆”的本领。以后做密码本、记分板、学生成绩管理系统、记账软件……都会用到它。它像 cin 和 cout 一样友好,只要记住“打开→读写→关闭”三步走,再顺手检查一下打开是否成功,你的程序就能和数据安稳做朋友啦!
例题精讲
以下关于C++文件读写的说法,哪个是正确的?
使用ofstream写入文件时,如果文件不存在,系统会自动创建该文件。
以下哪个函数用于从文件中读取一行字符串?
使用fstream对象同时读写文件时,需要在打开文件时指定模式为___。打开文件后,通常用___成员函数检查文件是否成功打开。