📖 故事引入:小强的数据保存烦恼
小强写了一个学生成绩管理系统,程序运行得好好的,输入5个学生的成绩,排序、统计、输出,一切完美。
可是,一关闭程序,所有数据就全没了!下次打开又要重新输入,太麻烦了!
"有没有办法让数据在程序关闭后还能保存下来,下次打开还能用呢?"小强挠了挠头。
让数据走出内存 —— 文件流、文本文件、二进制文件,一次讲透数据持久化的全部秘密。
通过小强的"数据丢失"烦恼,引出文件操作的必要性,用四大类比建立直观理解。
小强写了一个学生成绩管理系统,程序运行得好好的,输入5个学生的成绩,排序、统计、输出,一切完美。
可是,一关闭程序,所有数据就全没了!下次打开又要重新输入,太麻烦了!
"有没有办法让数据在程序关闭后还能保存下来,下次打开还能用呢?"小强挠了挠头。
内存像你的大脑,关机就忘;文件像笔记本,写上去就永久保存。
内存 → 大脑(临时) / 文件 → 笔记本(永久)数据从程序流向文件,就像水从水管流出。ifstream是进水口,ofstream是出水口。
ifstream ← 读 / ofstream → 写就像笔记本要有标签"数学笔记",文件也要有名字,不然找不到。
"score.txt" / "data.bin"文本文件像日记本(人类可读),二进制文件像保险柜(机器原样存储,人类看不懂)。
文本 = 日记 / 二进制 = 保险柜文件(File):是计算机中存储在外部介质(如硬盘)上的数据集合,程序关闭后数据仍然存在。
文件流(File Stream):C++ 中用流(Stream)的概念来操作文件。流是一系列字节的有序序列,数据从程序流向文件或从文件流向程序。
字节流抽象:文件被看作一连串的字节,程序按顺序读写这些字节。就像水管里的水,一个一个字节地流过。
EOF(End Of File):文件结束标志。当读取到文件末尾时,流的状态会变为"失败",可以用 eof() 方法检测。
深入理解文件的基本概念、文本文件与二进制文件的区别、文件流三大核心类及操作五步法。
文件是计算机中存储在外部介质上的数据集合,以文件为单位进行数据的读写操作。文件的主要特点:
C++ 中将文件分为两类:
| 文件类型 | 说明 | 适用场景 |
|---|---|---|
| 文本文件(Text File) | 以 ASCII / Unicode 编码存储,人类可直接阅读 | 配置文件、日志、数据记录 |
| 二进制文件(Binary File) | 以内存原样格式存储,人类无法直接阅读 | 图片、音频、结构体数据 |
文件名:如 "score.txt"、"data.bin"、"log.txt"
路径:文件在硬盘上的位置,如 "C:\\Users\\Admin\\score.txt" 或 "./data/score.txt"
"C:\\data\\score.txt" 而不是 "C:\data\score.txt"
EOF(End Of File)是文件结束的标志。在文本文件中,EOF 不是一个可见字符,而是一个由操作系统设置的标记。
在 C++ 中,可以通过以下方式检测 EOF:
#include <iostream>
#include <fstream>
int main() {
std::ifstream fin("data.txt");
char ch;
while (fin.get(ch)) { // 读取成功返回 true,到 EOF 返回 false
std::cout << ch;
}
return 0;
}
| 对比项 | 文本文件(Text) | 二进制文件(Binary) |
|---|---|---|
| 存储格式 | ASCII / Unicode 编码,每个字符占 1 字节(或更多) | 内存中的原始二进制格式,按数据类型原样存储 |
| 换行符处理 | 自动转换:\n → \r\n(Windows) | 不转换,原样写入 \n |
| 可读性 | 人类可直接用记事本打开阅读 | 人类无法直接阅读,需要用程序解析 |
| 空间效率 | 占用空间较大(如整数 123 存为 "123" 占 3 字节) | 占用空间较小(如 int 123 占 4 字节) |
| 精度 | 浮点数可能丢失精度(如 3.14159 存为字符串) | 完全保留原始精度 |
| 适用场景 | 配置文件、日志、文本数据 | 图片、音频、视频、结构体/类对象 |
| 打开模式 | 默认文本模式 | 必须加 ios::binary 标志 |
#include <iostream>
#include <fstream>
int main() {
// 文本文件:整数 123 存为字符 '1','2','3'(3字节)
std::ofstream fout1("text.txt");
fout1 << 123;
fout1.close();
// 二进制文件:整数 123 按 int 格式存(4字节)
std::ofstream fout2("binary.bin", std::ios::binary);
int num = 123; // 待写入的整数
fout2.write((char*)&num, sizeof(int));
fout2.close();
std::cout << "文本文件写入完成" << std::endl;
std::cout << "二进制文件写入完成" << std::endl;
return 0;
}
| 类名 | 功能 | 继承自 | 主要方法 |
|---|---|---|---|
| ifstream | 从文件读取数据 | istream | open(), close(), >>, getline(), read() |
| ofstream | 向文件写入数据 | ostream | open(), close(), <<, write(), put() |
| fstream | 对文件读写 | iostream | open(), close(), <<, >>, read(), write() |
| 模式标志 | 含义 | 说明 |
|---|---|---|
| ios::in | 只读 | 打开文件用于读取(ifstream 默认) |
| ios::out | 只写 | 打开文件用于写入(ofstream 默认),会清空原文件内容 |
| ios::app | 追加 | 在文件末尾追加写入,不会清空原内容 |
| ios::binary | 二进制模式 | 以二进制方式打开,不加则默认为文本模式 |
| ios::trunc | 截断 | 打开时清空文件内容(ios::out 默认行为) |
组合使用示例:
std::fstream fs;
fs.open("data.txt", std::ios::in); // 只读
fs.open("data.txt", std::ios::out); // 只写(清空)
fs.open("data.txt", std::ios::out | std::ios::app); // 追加写入
fs.open("data.bin", std::ios::binary); // 二进制模式
fs.open("data.txt", std::ios::in | std::ios::out); // 读写
#include <iostream>
#include <fstream>
#include <string>
int main() {
// 第1步:包含头文件 <fstream>
// 第2步:创建流对象
std::ofstream fout("hello.txt");
// 第3步:打开文件(ofstream 构造函数自动打开)
if (!fout.is_open()) {
std::cout << "打开文件失败!" << std::endl;
return 1;
}
// 第4步:写入数据
fout << "Hello, World!" << std::endl;
fout << "C++ 文件操作" << std::endl;
// 第5步:关闭文件
fout.close();
std::cout << "文件写入完成!" << std::endl;
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ofstream fout("score.txt");
if (!fout.is_open()) {
std::cout << "打开失败!" << std::endl;
return 1;
}
std::string name;
int score;
std::cout << "输入学生姓名和成绩(输入 q 结束):" << std::endl;
while (std::cin >> name >> score) {
if (name == "q") break;
fout << name << " " << score << std::endl;
}
fout.close();
std::cout << "成绩已保存到 score.txt" << std::endl;
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream fin("score.txt");
if (!fin.is_open()) {
std::cout << "打开失败!" << std::endl;
return 1;
}
std::string line;
int count = 0;
std::cout << "=== 文件内容 ===" << std::endl;
while (std::getline(fin, line)) { // 逐行读取
count++;
std::cout << "第" << count << "行: " << line << std::endl;
}
fin.close();
std::cout << "共读取 " << count << " 行" << std::endl;
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream fin("score.txt");
if (!fin.is_open()) {
std::cout << "打开失败!" << std::endl;
return 1;
}
std::string name;
int score;
double sum = 0;
int count = 0;
while (fin >> name >> score) { // 按空格/换行分隔读取
std::cout << name << ": " << score << " 分" << std::endl;
sum += score;
count++;
}
std::cout << "平均分: " << sum / count << std::endl;
fin.close();
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
int main() {
std::ofstream fout("log.txt", std::ios::app); // 追加模式
if (!fout.is_open()) {
std::cout << "打开失败!" << std::endl;
return 1;
}
// 每次运行程序都会追加新日志,不会覆盖旧日志
std::time_t now = std::time(nullptr);
fout << "[" << now << "] 程序运行了一次\n";
fout.close();
std::cout << "日志已追加到 log.txt" << std::endl;
return 0;
}
#include <iostream>
#include <fstream>
struct Student {
char name[50]; // 定长字符数组:可安全二进制读写
int score;
};
int main() {
Student s = {"小明", 95};
// 二进制写入
std::ofstream fout("student.bin", std::ios::binary);
fout.write((char*)&s, sizeof(Student));
fout.close();
// 二进制读取
Student s2;
std::ifstream fin("student.bin", std::ios::binary);
fin.read((char*)&s2, sizeof(Student));
fin.close();
std::cout << s2.name << ": " << s2.score << std::endl;
return 0;
}
通过3道综合题,巩固文件读写操作的实际应用能力。
题目描述:编写程序,从键盘输入5个学生的姓名和成绩,保存到文件 "score.txt" 中。然后重新打开该文件,读取所有学生的成绩,计算平均分并输出。
#include <iostream>
#include <fstream>
#include <string>
int main() {
// 写入阶段
std::ofstream fout("score.txt");
std::string name;
int score;
for (int i = 0; i < 5; i++) {
std::cout << "输入第" << (i+1) << "个学生姓名: ";
std::cin >> name;
std::cout << "输入成绩: ";
std::cin >> score;
fout << name << " " << score << std::endl;
}
fout.close();
// 读取阶段
std::ifstream fin("score.txt");
double sum = 0;
std::string n;
int s;
while (fin >> n >> s) {
std::cout << n << ": " << s << " 分" << std::endl;
sum += s;
}
fin.close();
std::cout << "平均分: " << sum / 5 << std::endl;
return 0;
}
输入第1个学生姓名: 小明
输入成绩: 95
输入第2个学生姓名: 小红
输入成绩: 88
输入第3个学生姓名: 小刚
输入成绩: 92
输入第4个学生姓名: 小丽
输入成绩: 90
输入第5个学生姓名: 小强
输入成绩: 85
小明: 95 分
小红: 88 分
小刚: 92 分
小丽: 90 分
小强: 85 分
平均分: 90
题目描述:编写程序,模拟日志记录功能。每次运行程序时,向 "log.txt" 追加一条记录,包含时间戳和操作内容。追加3条记录后,读取并输出所有日志。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ofstream fout("log.txt", std::ios::app);
if (!fout.is_open()) {
std::cout << "打开失败!" << std::endl;
return 1;
}
std::string ops[] = {"用户登录", "修改密码", "退出系统"};
for (int i = 0; i < 3; i++) {
fout << "[LOG] " << ops[i] << std::endl;
}
fout.close();
std::ifstream fin("log.txt");
std::string line;
std::cout << "=== 日志内容 ===" << std::endl;
while (std::getline(fin, line)) {
std::cout << line << std::endl;
}
fin.close();
return 0;
}
=== 日志内容 ===
[LOG] 用户登录
[LOG] 修改密码
[LOG] 退出系统
题目描述:编写程序,读取文件 "text.txt",统计其中的行数、单词数和字符数,并输出统计结果。
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
int main() {
std::ifstream fin("text.txt");
if (!fin.is_open()) {
std::cout << "打开失败!" << std::endl;
return 1;
}
int lines = 0, words = 0, chars = 0;
std::string line, word;
while (std::getline(fin, line)) {
lines++;
chars += line.size();
std::istringstream iss(line);
while (iss >> word) words++;
}
fin.close();
std::cout << "行数: " << lines << std::endl;
std::cout << "单词数: " << words << std::endl;
std::cout << "字符数: " << chars << std::endl;
return 0;
}
行数: 2
单词数: 3
字符数: 19
回顾本课核心知识点,掌握文件操作的万能模板,拓展思考方向。
| 知识点 | 关键内容 |
|---|---|
| 文件定义 | 存储在硬盘上的数据集合,程序关闭后不丢失 |
| 字节流 | 文件被看作连续的字节序列,按顺序读写 |
| 文本文件 | ASCII编码,人类可读,换行符自动转换,适合配置/日志 |
| 二进制文件 | 内存原样存储,人类不可读,需加 ios::binary,适合图像/结构体 |
| ifstream | 从文件读取数据,继承自 istream |
| ofstream | 向文件写入数据,继承自 ostream,默认清空原文件 |
| fstream | 可读写文件,继承自 iostream |
| 五步法 | 包含头文件 → 创建对象 → 打开文件 → 读写 → 关闭 |
| ios::app | 追加模式,在文件末尾写入,不清空原内容 |
| is_open() | 检查文件是否成功打开,必须检查! |
#include <iostream>
#include <fstream>
#include <string>
int main() {
// 写入模板
std::ofstream fout("output.txt");
if (!fout.is_open()) {
std::cout << "写入文件打开失败!" << std::endl;
return 1;
}
fout << "写入内容" << std::endl;
fout.close();
// 读取模板
std::ifstream fin("output.txt");
if (!fin.is_open()) {
std::cout << "读取文件打开失败!" << std::endl;
return 1;
}
std::string line;
while (std::getline(fin, line)) {
std::cout << line << std::endl;
}
fin.close();
return 0;
}
1. 为什么文件操作必须先检查 is_open()?如果不检查会怎样?
2. 文本文件和二进制文件在读写整数时有什么区别?为什么二进制文件更高效?
3. ios::app 和 ios::out 有什么区别?什么场景下用追加模式?
4. 如果忘记关闭文件,会发生什么?为什么?
5. 如何用文件操作实现一个简单的"学生成绩管理系统"?(提示:结合结构体和文件)
检测本课学习成果,掌握5大易错点和速查表,巩固关键知识点。
std::ifstream fin("not_exist.txt");
fin >> x; // 文件不存在,fin 为无效状态
// 直接读取会导致未定义行为
std::ifstream fin("not_exist.txt");
if (!fin.is_open()) {
std::cout << "文件打开失败!" << std::endl;
return 1;
}
fin >> x; // 安全读取
std::ifstream fin("C:\data\score.txt");
// \d 被解释为转义字符,文件名错误
std::ifstream fin("C:\\data\\score.txt");
// 双反斜杠 \\\\ 转义为单个反斜杠
// 或使用正斜杠: "C:/data/score.txt"
std::ofstream fout("data.txt");
fout << "新内容";
// data.txt 原有内容被清空!
std::ofstream fout("data.txt", std::ios::app);
fout << "追加内容";
// 在末尾追加,不清空原内容
std::ofstream fout("data.bin");
fout.write((char*)&s, sizeof(s));
// 文本模式下换行符会被转换,数据损坏
std::ofstream fout("data.bin", std::ios::binary);
fout.write((char*)&s, sizeof(s));
// 二进制模式,数据原样写入
std::ofstream fout("data.txt");
fout << "重要数据";
// 没有 close(),数据可能还在缓冲区未写入
// 程序结束时自动关闭,但不保证数据已写入
std::ofstream fout("data.txt");
fout << "重要数据";
fout.close(); // 确保数据写入磁盘
| 操作 | 代码 |
|---|---|
| 包含头文件 | #include <fstream> |
| 创建只读流 | ifstream fin("file.txt"); |
| 创建只写流(清空) | ofstream fout("file.txt"); |
| 创建追加流 | ofstream fout("file.txt", ios::app); |
| 创建二进制流 | fstream fs("file.bin", ios::binary); |
| 检查文件是否打开 | if (!fin.is_open()) { ... } |
| 写入数据 | fout << data; |
| 读取数据 | fin >> data; |
| 逐行读取 | getline(fin, line); |
| 二进制写入 | fout.write((char*)&obj, sizeof(obj)); |
| 二进制读取 | fin.read((char*)&obj, sizeof(obj)); |
| 关闭文件 | fin.close(); |
| 检测 EOF | if (fin.eof()) { ... } |
存储在硬盘上的数据集合,以文件为单位进行读写。程序关闭后数据不丢失。
C++ 用流(Stream)操作文件。ifstream 读,ofstream 写,fstream 读写。
ASCII 编码,人类可读。换行符自动转换。用 << 和 >> 操作。
内存原样存储,需加 ios::binary。用 write() 和 read() 操作。效率高。
包含 → 创建 → 打开 → 读写 → 关闭。缺一不可!
路径反斜杠转义、is_open 检查、忘记 close 导致数据丢失、二进制必须加 binary。
通过分层练习巩固文件操作知识,从基础到挑战,循序渐进。
编写程序,创建一个名为 "hello.txt" 的文件,向其中写入 "Hello, C++ File!",然后关闭文件。最后检查文件是否写入成功。
#include <iostream>
#include <fstream>
int main() {
std::ofstream fout("hello.txt");
if (!fout.is_open()) {
std::cout << "创建失败!" << std::endl;
return 1;
}
fout << "Hello, C++ File!" << std::endl;
fout.close();
std::cout << "文件写入成功!" << std::endl;
return 0;
}
假设已有文件 "data.txt",内容为多行文本。编写程序读取该文件并逐行输出到控制台。
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
int main() {
std::ifstream fin("data.txt");
if (!fin.is_open()) {
std::cout << "打开失败!" << std::endl;
return 1;
}
std::string line;
while (std::getline(fin, line)) {
std::cout << line << std::endl;
}
fin.close();
return 0;
}
从键盘输入5个学生的成绩,保存到 "score.txt" 中,然后读取文件,计算平均分、最高分和最低分,将统计结果写入 "result.txt"。
#include <iostream>
#include <fstream>
#include <algorithm>
int main() {
int scores[5];
std::ofstream fout("score.txt");
for (int i = 0; i < 5; i++) {
std::cin >> scores[i];
fout << scores[i] << std::endl;
}
fout.close();
int sum = 0, mx = scores[0], mn = scores[0];
std::ifstream fin("score.txt");
int s;
while (fin >> s) {
sum += s;
mx = std::max(mx, s);
mn = std::min(mn, s);
}
fin.close();
std::ofstream fout2("result.txt");
fout2 << "平均分: " << sum / 5 << std::endl;
fout2 << "最高分: " << mx << std::endl;
fout2 << "最低分: " << mn << std::endl;
fout2.close();
return 0;
}
从键盘输入若干个单词(以 "end" 结束),将每个单词占一行的方式写入文件 "words.txt"。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ofstream fout("words.txt");
std::string w;
while (std::cin >> w && w != "end") {
fout << w << std::endl;
}
fout.close();
std::cout << "写入完成!" << std::endl;
return 0;
}
思路:ofstream 打开文件,while (cin >> w && w != "end") 逐词读入并写入文件,最后 close。
易错:忘了 fout.close(),缓冲区没刷新,文件内容可能不完整。
读取文件 "text.txt",统计其中的字符总数(包括空格和换行符),并输出结果。
#include <iostream>
#include <fstream>
int main() {
std::ifstream fin("text.txt");
int count = 0;
char ch;
while (fin.get(ch)) count++;
fin.close();
std::cout << "字符总数: " << count << std::endl;
return 0;
}
思路:fin.get(ch) 逐字符读取(空格、换行都算),每读到一个计数加一。
易错:用 fin >> ch 读——它会自动跳过空格和换行,统计出来的数偏少。
先以 ios::out 模式写入 "A" 到文件 test.txt,再以 ios::app 模式追加 "B" 到同一文件,最后读取并输出全部内容。观察两次写入的结果。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ofstream fout("test.txt");
fout << "A"; // ios::out 默认清空
fout.close();
std::ofstream fout2("test.txt", std::ios::app);
fout2 << "B"; // 追加
fout2.close();
std::ifstream fin("test.txt");
std::string s;
while (fin >> s) std::cout << s;
fin.close();
std::cout << std::endl;
return 0;
}
思路:ios::out(默认)打开会清空原内容,ios::app 在文件末尾追加,用两次写入对比效果。
易错:以为 ofstream 默认就是追加模式,结果原文件内容被清空了。
以下哪种路径写法是正确的?A. "C:\data\file.txt" B. "C:\\data\\file.txt" C. "C:/data/file.txt" D. B和C都对
答案:D。B 使用双反斜杠转义,C 使用正斜杠(Windows 也支持),A 中的 \d 会被解释为转义字符,是错误的。
考点:字符串转义——路径中的反斜杠要写成 \\,或直接用正斜杠 /。
易错:以为单个反斜杠能直接写进路径字符串,\d 这类不存在的转义会出错。
使用结构体 Student(含 name 和 score),实现:从键盘输入3个学生的信息,保存到文件 students.dat(二进制),然后读取并输出所有学生信息。
#include <iostream>
#include <fstream>
struct Student {
char name[50]; // 定长字符数组:可安全二进制读写
int score;
};
int main() {
Student students[3];
for (int i = 0; i < 3; i++) {
std::cin >> students[i].name >> students[i].score;
}
std::ofstream fout("students.dat", std::ios::binary);
for (int i = 0; i < 3; i++) {
fout.write((char*)&students[i], sizeof(Student));
}
fout.close();
Student s;
std::ifstream fin("students.dat", std::ios::binary);
while (fin.read((char*)&s, sizeof(Student))) {
std::cout << s.name << ": " << s.score << std::endl;
}
fin.close();
return 0;
}
思路:结构体用定长字符数组做成员,就可以安全地 write/read 整块读写。
易错:string 成员直接二进制读写会出问题——它内部存的是指针,不是字符内容。
读取文件 "article.txt",统计每个单词出现的次数,输出出现次数最多的单词及其次数。
#include <iostream>
#include <fstream>
#include <string>
#include <map>
int main() {
std::ifstream fin("article.txt");
std::map<std::string, int> freq;
std::string w;
while (fin >> w) freq[w]++;
fin.close();
std::string max_word;
int max_count = 0;
for (auto &p : freq) {
if (p.second > max_count) {
max_count = p.second;
max_word = p.first;
}
}
std::cout << max_word << ": " << max_count << std::endl;
return 0;
}
思路:map
易错:忘了判断文件是否打开成功,文件不存在时程序静默输出空结果。
编写程序,将文件 "source.txt" 的内容完整拷贝到 "copy.txt" 中。要求:先检查源文件是否存在,拷贝完成后验证拷贝结果。
#include <iostream>
#include <fstream>
int main() {
std::ifstream fin("source.txt");
if (!fin.is_open()) {
std::cout << "源文件不存在!" << std::endl;
return 1;
}
std::ofstream fout("copy.txt");
char ch;
int count = 0;
while (fin.get(ch)) {
fout.put(ch);
count++;
}
fin.close();
fout.close();
std::cout << "拷贝完成,共拷贝 " << count << " 字节" << std::endl;
return 0;
}
思路:先检查源文件 is_open(),再逐字符从 fin 读出、写入 fout 完成拷贝。
易错:不检查 is_open,源文件不存在时静默拷贝出一个空文件。
读取文件 "data.txt",分别统计行数、单词数和字符数(不含空格),输出统计结果。
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
int main() {
std::ifstream fin("data.txt");
int lines = 0, words = 0, chars = 0;
std::string line, w;
while (std::getline(fin, line)) {
lines++;
for (char c : line) {
if (c != ' ') chars++;
}
std::istringstream iss(line);
while (iss >> w) words++;
}
fin.close();
std::cout << "行数: " << lines << std::endl;
std::cout << "单词数: " << words << std::endl;
std::cout << "非空格字符数: " << chars << std::endl;
return 0;
}
思路:getline 按行读统计行数,再把每行交给 stringstream 拆单词统计词数。
易错:getline 和 >> 混用在同一个流上会互相干扰(>> 留下的换行被 getline 读成空行)。
使用结构体 Student(含 id, name, score),实现:(1) 输入 N 个学生的信息并保存到文件 students.dat(二进制);(2) 从文件读取所有学生,按成绩降序排序后输出;(3) 将排序结果写入 sorted.dat。
#include <iostream>
#include <fstream>
#include <algorithm>
struct Student {
int id;
char name[50]; // 定长字符数组:可安全二进制读写
int score;
};
int main() {
int n;
std::cin >> n;
Student *arr = new Student[n];
for (int i = 0; i < n; i++) {
std::cin >> arr[i].id >> arr[i].name >> arr[i].score;
}
std::ofstream fout("students.dat", std::ios::binary);
for (int i = 0; i < n; i++) {
fout.write((char*)&arr[i], sizeof(Student));
}
fout.close();
std::sort(arr, arr + n, [](const Student& a, const Student& b) {
return a.score > b.score;
});
std::ofstream fout2("sorted.dat", std::ios::binary);
for (int i = 0; i < n; i++) {
fout2.write((char*)&arr[i], sizeof(Student));
}
fout2.close();
for (int i = 0; i < n; i++) {
std::cout << arr[i].name << ": " << arr[i].score << std::endl;
}
delete[] arr;
return 0;
}
思路:把学生数据写入文件保存,再读回来排序输出,实现持久化。
易错:new 的数组忘了 delete[];文件用完忘 close 导致数据没写完。
读取文件 "input.txt" 的全部内容,将内容完全反转(最后一个字符变成第一个),写入 "output.txt"。例如 "ABC" → "CBA"。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream fin("input.txt");
std::string content;
char ch;
while (fin.get(ch)) content += ch;
fin.close();
std::reverse(content.begin(), content.end());
std::ofstream fout("output.txt");
fout << content;
fout.close();
return 0;
}
思路:把文件全部内容读进一个 string,reverse 后写回另一个文件。
易错:整体反转会把换行符的位置也反过来,行结构被打乱——做题前先确认是按整体反转还是按行反转。
使用结构体 Contact(含 name, phone, email),实现:(1) 从键盘输入若干联系人信息保存到 contacts.dat(二进制);(2) 按姓名搜索联系人并输出;(3) 将所有联系人按姓名排序后输出到控制台。
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <cstring>
struct Contact {
char name[50], phone[20], email[50]; // 定长字符数组:可安全二进制读写
};
int main() {
std::vector<Contact> contacts;
std::string name;
while (std::cin >> name && name != "end") {
Contact c;
strcpy(c.name, name.c_str());
std::cin >> c.phone >> c.email;
contacts.push_back(c);
}
std::ofstream fout("contacts.dat", std::ios::binary);
for (auto &c : contacts) {
fout.write((char*)&c, sizeof(Contact));
}
fout.close();
std::sort(contacts.begin(), contacts.end(),
[](const Contact& a, const Contact& b) {
return strcmp(a.name, b.name) < 0;
});
for (auto &c : contacts) {
std::cout << c.name << " " << c.phone << " " << c.email << std::endl;
}
return 0;
}
思路:vector
易错:把 string 成员直接 write 进二进制文件,写入的是指针地址而不是姓名内容。
假设有一个日志文件 "server.log",每行格式为 "时间戳 操作类型 状态码"(如 "2024-01-01 10:00:00 LOGIN 200")。编写程序统计:(1) 总请求数;(2) 每种操作类型的次数;(3) 状态码为 404 和 500 的请求数;(4) 将统计结果写入 report.txt。
#include <iostream>
#include <fstream>
#include <string>
#include <map>
int main() {
std::ifstream fin("server.log");
int total = 0, err404 = 0, err500 = 0;
std::map<std::string, int> op_count;
std::string ts, op, code;
while (fin >> ts >> op >> code) {
total++;
op_count[op]++;
if (code == "404") err404++;
if (code == "500") err500++;
}
fin.close();
std::ofstream fout("report.txt");
fout << "总请求数: " << total << std::endl;
for (auto &p : op_count) {
fout << p.first << ": " << p.second << std::endl;
}
fout << "404错误: " << err404 << std::endl;
fout << "500错误: " << err500 << std::endl;
fout.close();
return 0;
}
思路:逐行读日志,按空格拆字段,map 统计各操作次数和 404/500 错误数。
易错:用 >> 按空白切分时字段数不固定的日志会错位,要按固定格式仔细对齐。