文件读写与异常处理
一、课上练习
编程练习
二、知识总结
✨ 文件重定向
文件重定向是指将程序的控制台输入输出改为文件输入输出。通过文件重定向,程序可以从文件中读取数据,也可以将结果写入文件,而不需要修改程序中的 cin 和 cout 语句。
✨ C++ 文件输入输出
使用 C++ 风格的文件操作需要添加头文件 并使用 std 命名空间。
#include <fstream>
using namespace std;文件输入
使用 ifstream 创建读取变量,通过 open 方法打开文件,读取完毕后使用 close 关闭文件。
1ifstream cin; // 创建读取变量
2cin.open("in.txt"); // 打开文件
3if (!cin.is_open()) {
4 cout << "can't find file" << endl;
5}
6int number;
7cin >> number; // 读取数据
8cin.close(); // 关闭文件文件输出
使用 ofstream 创建写入变量并打开输出文件,如果文件不存在则自动创建文件。
ofstream cout("out.txt"); // 创建写入变量并打开输出文件
cout << "hello" << endl; // 写入数据
cout.close(); // 关闭文件文件读写
fstream 支持同时进行文件的读取和写入操作,可以选择追加写入或清空写入两种模式。
追加写入方式,即在文件末尾添加写入的内容:
fstream file("in.txt", ios::in | ios::out | ios::app);
int number;
file >> number;
file << "output";
file.close();清空写入方式,即先清空文件内容再进行写入操作:
fstream file("in.txt", ios::in | ios::out | ios::trunc);
int number;
file >> number;
file << "output";
file.close();✨ C 语言文件输入输出
使用 C 语言风格的文件重定向无需特别添加头文件,使用 freopen 函数即可将标准输入输出重定向到文件。
freopen("in.txt","r", stdin);
freopen("out.txt","w", stdout);这种方式的优点是代码简洁,重定向后程序中所有的 cin/scanf 和 cout/printf 都会自动从文件读写,在竞赛中较为常用。
✨ 异常处理
程序异常概念
程序异常就是程序的非编译性错误,可能是系统定义的,也可能是程序员定义的。程序的异常处理让程序在发生错误后依旧可以正常运行后续程序,提高了程序的健壮性。
异常处理的结构
异常处理包含三个关键模块:
- 抛出(throw):当检测到异常情况时,程序会使用
throw关键字抛出一个异常 - 尝试(try):
try代码块包围可能抛出异常的代码,并指定一个或多个catch块来处理可能发生的异常 - 捕获(catch):异常被抛出后,程序会在
catch代码块中捕获并处理这个异常
try-catch 的基本结构如下:
1try {
2 // 可能抛出异常的代码
3} catch (exception &error) {
4 // 处理特定类型的异常
5} catch (...) {
6 // 处理所有其他类型的异常
7}其中 throw 可以在 try 模块中直接使用,也可以在 try 模块调用的函数中使用。
示例代码
下面展示在 try 模块中直接抛出异常的用法:
1#include <iostream>
2#include <stdexcept>
3using namespace std;
4
5int main() {
6 int a, b;
7 cin >> a >> b;
8 try {
9 if (b == 0) {
10 throw runtime_error("divide zero");
11 }
12 if (a < b) {
13 throw invalid_argument("a < b");
14 }
15 if (a > 100 || b > 100) {
16 throw invalid_argument("out of range");
17 }
18 int result = a / b;
19 cout << a << " / " << b << " = " << result << endl;
20 } catch (runtime_error &error) {
21 cout << error.what() << endl;
22 } catch (invalid_argument &error) {
23 cout << error.what() << endl;
24 } catch (...) {
25 cout << "an unknow error" << endl;
26 }
27 return 0;
28}下面展示在函数中抛出异常、在 try 模块中调用该函数的用法:
1#include <iostream>
2#include <stdexcept>
3using namespace std;
4
5int divide(int a, int b) {
6 if (b == 0) {
7 throw runtime_error("divide zero");
8 }
9 if (a < b) {
10 throw invalid_argument("a < b");
11 }
12 if (a > 100 || b > 100) {
13 throw invalid_argument("out of range");
14 }
15 int result = a / b;
16 return result;
17}
18
19int main() {
20 int a, b;
21 cin >> a >> b;
22 try {
23 int result = divide(a, b);
24 cout << a << " / " << b << " = " << result << endl;
25 } catch (runtime_error &error) {
26 cout << error.what() << endl;
27 } catch (invalid_argument &error) {
28 cout << error.what() << endl;
29 } catch (...) {
30 cout << "an unknow error" << endl;
31 }
32 return 0;
33}异常处理与文件操作
异常处理常与文件操作结合使用,用于处理文件打开失败等情况:
1#include <iostream>
2#include <fstream>
3#include <stdexcept>
4using namespace std;
5
6int main() {
7 try {
8 ifstream fin("in.txt");
9 if (!fin.is_open()) {
10 throw runtime_error("no file");
11 }
12 } catch (runtime_error &e) {
13 cout << e.what() << endl;
14 }
15 return 0;
16}✨ 文件读写的执行示例
下面通过具体例子展示文件读写和异常处理的完整操作流程。
例1:C++ 风格文件输入输出的完整流程
假设文件 in.txt 的内容为:
3
10 20 30程序需要从文件读入数据,计算总和,输出到 out.txt:
1#include <fstream>
2using namespace std;
3
4int main() {
5 // 第一步:打开输入文件
6 ifstream fin("in.txt");
7
8 // 第二步:读取数据
9 int n;
10 fin >> n; // 读到 n = 3
11 int sum = 0;
12 for (int i = 0; i < n; ++i) {
13 int x;
14 fin >> x; // 依次读到 10, 20, 30
15 sum += x;
16 }
17 fin.close(); // 第三步:关闭输入文件
18
19 // 第四步:打开输出文件并写入结果
20 ofstream fout("out.txt");
21 fout << "sum = " << sum << endl; // 写入 "sum = 60"
22 fout.close(); // 第五步:关闭输出文件
23
24 return 0;
25}1执行流程:
21. 打开 in.txt → 成功
32. 读取 n=3
43. 循环读取:x=10, sum=10 → x=20, sum=30 → x=30, sum=60
54. 关闭 in.txt
65. 打开 out.txt(不存在则自动创建)
76. 写入 "sum = 60"
87. 关闭 out.txt
9
10最终 out.txt 内容:
11sum = 60例2:C 语言 freopen 重定向的完整流程
1#include <cstdio>
2using namespace std;
3
4int main() {
5 freopen("in.txt", "r", stdin); // 将标准输入重定向到 in.txt
6 freopen("out.txt", "w", stdout); // 将标准输出重定向到 out.txt
7
8 int a, b;
9 scanf("%d%d", &a, &b); // 实际从 in.txt 读取
10 printf("%d\n", a + b); // 实际写入 out.txt
11
12 return 0;
13}执行流程:
1. freopen 将 stdin 指向 in.txt → 之后所有 scanf/cin 都从 in.txt 读取
2. freopen 将 stdout 指向 out.txt → 之后所有 printf/cout 都写入 out.txt
3. 程序中的输入输出代码无需任何修改,和控制台读写完全一样竞赛中的常用写法: 很多竞赛题目要求文件输入输出,使用 freopen 只需在程序开头加两行代码,其余代码不用改动,非常方便。例3:异常处理的执行流程
1int a = 10, b = 0;
2try {
3 if (b == 0) {
4 throw runtime_error("divide zero"); // 抛出异常
5 }
6 cout << a / b << endl; // 这行不会执行
7} catch (runtime_error &error) {
8 cout << error.what() << endl; // 捕获并处理异常
9}
10cout << "程序继续运行" << endl; // 异常处理后程序正常继续1执行流程:
21. 进入 try 块
32. 检查 b == 0 → 条件为真
43. throw 抛出 runtime_error 异常 → 立即跳出 try 块
54. a / b 这行被跳过(不会执行)
65. 进入匹配的 catch 块,输出 "divide zero"
76. catch 块执行完毕,程序继续正常运行
87. 输出 "程序继续运行"关键理解: throw 之后,try 块中剩余的代码不会执行,程序直接跳到匹配的 catch 块。异常处理完后,程序从 catch 块之后的代码继续运行。✨ 文件读写的解题步骤
文件读写题目的解题步骤:
- 确定输入输出方式:判断题目要求使用 C++ 风格(ifstream/ofstream)还是 C 风格(freopen)
- 打开文件:根据方式选择合适的打开方法
- 检查文件是否打开成功:使用
is_open()或异常处理判断文件是否存在 - 读写数据:文件打开后,读写语法和控制台输入输出基本相同
- 关闭文件:操作完成后务必关闭文件
异常处理题目的解题步骤:
- 识别可能出错的操作:如除以零、文件不存在、数组越界等
- 用 try 包裹可能出错的代码
- 在可能出错的位置使用 throw 抛出异常
- 用 catch 捕获并处理异常:可以有多个 catch 块处理不同类型的异常
- 用
catch(...)作为兜底:捕获所有未被前面 catch 块处理的异常
✨ 文件读写的常见错误
- 忘记关闭文件:打开文件后一定要调用
close(),否则可能导致数据没有完整写入文件(缓冲区未刷新) - 文件路径错误:程序默认在可执行文件所在目录查找文件,如果文件在其他位置需要使用完整路径
- freopen 后忘记是重定向了:使用
freopen后,cin/cout已经重定向到文件,此时如果想在控制台输出调试信息需要用cerr - catch 块顺序不当:多个 catch 块按从上到下的顺序匹配,如果
catch(...)放在前面,后面的特定类型 catch 块永远不会执行 - throw 后还写了需要执行的代码:
throw之后 try 块中的代码不会执行,需要执行的清理代码应该放在 catch 块中或 try-catch 之后 - ifstream 变量名与 cin 重名:示例中使用
ifstream cin;是为了演示方便,实际编程中建议使用不同的变量名(如fin),避免与标准输入的cin混淆
三、课后练习
基础知识练习
- 文件读写与异常处理 - 选择题## 编程练习