C++启蒙课程:第17章  输入、输出和文件

我们将学习数据“小水滴”如何在“管道”中流动,控制“缓冲区”,并使用“记忆卷轴”(文件)实现永久存储!

🌊

管道与缓冲区 (cout)

学习“数据水流”如何进入“暂存水桶”(缓冲区),以及 `endl` 如何“立即倾倒”。

🚦

智能水闸 (cin)

掌握“守卫机器人”`cin`如何检查输入,以及 `fail()` 状态灯和三步修复魔法。

📜

记忆卷轴 (fstream)

学习“文件管理员” `ofstream` 和 `ifstream` 如何读写“永久记忆卷轴”。

课时一:管道与缓冲区:流模型与输出

(覆盖知识点 17.1 - 17.2.4)

17.1.1 “暂存水桶” (缓冲区)

数据(水滴)先进入“暂存水桶”(缓冲区)。水桶满或 `endl`(立即倾倒)时,才真正发送到屏幕。

输出缓冲区 (水桶):
// 17.2.3 刷新 // endl 会换行并“立即倾倒”缓冲区 cout << "Hello" << endl; // \n 只换行, 不保证立即倾倒 cout << "World\n";

17.2.4 格式化“展示位”

`width()` 机器人为下一个项目划定“展示位”宽度。`precision()` 则控制小数点后的位数。

double pi = 3.14159265; // 17.2.4 设置总字段宽度 cout.width(12); // 17.2.4 设置小数点后精度 cout.precision(5); cout << pi; // 输出: [ 3.14159]

课时二:智能水闸:输入与流状态

(覆盖知识点 17.2.2, 17.3)

17.3.2 “状态灯” (Stream States)

`cin` 有一个“状态灯”。输入正确时是 🟢 (good)。输入类型不匹配(如输入 "abc" 给 `int`)时,灯变 🟡 (fail)。

...
int num; cin >> num; // 17.3.2 检查状态灯 if (cin.fail()) { // 状态灯变为 🟡 (fail) cout << "输入不匹配!"; } else if (cin.good()) { // 状态灯为 🟢 (good) }

17.3.3 “三步修复魔法”

当状态灯变 🟡 (fail) 时,`cin` 会卡住。你必须用“三步魔法”来修复它:

输入缓冲区:
[a][b][c][\n]
1. cin.clear() (重置 🟡 -> 🟢)
2. cin.ignore() (启动吸尘器 🌪️)
3. 提示重新输入...
while (!(cin >> number)) { // 魔法 1: 重置状态灯 cin.clear(); // 魔法 2: 清理缓冲区 (吸尘) cin.ignore(100, '\n'); // 魔法 3: 重新提示 cout << "请重新输入: "; }

课时三:记忆卷轴:文件I/O与模式

(覆盖知识点 17.4.1 - 17.4.5)

17.4.1 “永久记忆卷轴” (fstream)

`ofstream` (写入) 和 `ifstream` (读取) 是“文件管理员”,帮你读写“记忆卷轴”。必须用 `is_open()` 检查是否成功。

#include <fstream> // 17.4.1 写入管理员 ofstream fout; // 17.4.5 追加模式 (不覆盖) fout.open("diary.txt", ios::app); // 17.4.2 检查 if (!fout.is_open()) { ... } fout << "New line.\n"; fout.close();

课时四:格式魔法与数据定位

(覆盖知识点 17.2.4, 17.5, 17.4.6)

17.2.4 “格式魔法开关” (setf)

`setf()` 机器人帮你按下“格式开关”。例如 `ios_base::fixed` 强制使用定点表示法(非科学计数法)。

123.45678
double val = 123.45678; // 17.2.4 按下 "定点" 开关 cout.setf(ios_base::fixed, ios_base::floatfield); cout.precision(2); // 输出: 123.46 // 17.2.4 取消设置 cout.unsetf(ios_base::fixed);

17.4.6 “数据定位GPS” (seek)

`seekg()` 就像GPS,允许你“瞬移”到文件的任意位置开始读取,而不是从头开始。

File:
📍
A
B
C
D
E
ifstream fin("file.txt"); // A B C D E // 17.4.6 "GPS" 瞬移 // 从开头(beg) 偏移 2 个 fin.seekg(2, ios_base::beg); char ch; fin.get(ch); // 读取到 'C'

编程实践与作业

是时候检验你作为“I/O流控制大师”的实力了!

练习 1:格式化输出 (17.2.4)

任务:

使用 width()precision() 方法打印一个浮点数,要求总宽度为12,小数点后保留5位。

点击查看参考答案
#include <iostream> using namespace std; int main() { double pi = 3.1415926535; // 17.2.4 设置小数点后精度 cout.precision(5); // 17.2.4 设置总字段宽度 cout.width(12); cout << pi << endl; // 输出: [ 3.14159] (前面有空格) return 0; }
练习 2:流状态恢复 (17.3)

任务:

编写一个程序,要求用户输入一个整数。如果输入失败(用户输入了字母),程序必须捕获 fail 状态,使用“三步修复魔法”(clear(), ignore(), 重新提示)来恢复输入流。

点击查看参考答案
#include <iostream> using namespace std; int main() { int number; cout << "请输入一个整数: "; while (!(cin >> number)) { // 17.3.2 检查状态灯 cout << "警告! 输入无效。" << endl; // 17.3.3 三步修复魔法 cin.clear(); // 1. 重置状态灯 cin.ignore(100, '\n'); // 2. 清理缓冲区 cout << "请重新输入: "; // 3. 重新提示 } cout << "成功读取: " << number << endl; return 0; }
练习 3:文件追加 (17.4.5)

任务:

编写一个程序,要求用户输入一行文本,并将该文本写入到 "diary.txt" 中(使用 ios::app 追加模式)。

点击查看参考答案
#include <iostream> #include <fstream> // 17.4.1 #include <string> using namespace std; int main() { string entry; getline(cin, entry); ofstream fout; // 17.4.5 使用 ios::app 模式 fout.open("diary.txt", ios::app); if (!fout.is_open()) { // 17.4.2 return 1; // 打开失败 } fout << entry << endl; fout.close(); return 0; }
练习 4:格式魔法 (17.2.4)

任务:

使用 setf()precision() 强制以定点格式显示 double 值,并保留小数点后 3 位。

点击查看参考答案
#include <iostream> using namespace std; int main() { double amount = 123.456789; // 17.2.4 设置定点 cout.setf(ios_base::fixed, ios_base::floatfield); cout.precision(3); // 精度 3 cout << "定点格式: " << amount << endl; // 输出: 123.457 (四舍五入) return 0; }

本章知识点总结与复习

核心概念 解释/功能 关键用法/示例
流 (Stream)程序和I/O设备之间的数据传输抽象。istream, ostream
缓冲区 (Buffer)临时存储区域,提高I/O效率。endl (刷新缓冲区)
流状态描述流操作是否成功的标志 (Good, EOF, Fail, Bad)。cin.fail()
状态恢复三步魔法:`clear()` (重置), `ignore()` (清理)。cin.clear(); cin.ignore();
文件I/O通过 `ifstream` (读) 和 `ofstream` (写) 交互。#include <fstream>
文件模式控制文件打开方式,如 `ios::app` (追加)。fout.open(..., ios::app);
格式化使用 `width()`, `precision()`, `setf()` 控制输出外观。cout.setf(ios_base::fixed);
随机存取直接跳转到文件中任何位置进行读写。fin.seekg(), fout.seekp()