cs106l-lecture-2-streams
Course: 斯坦福大学: C++标准库编程 | C++ Standard Library Programming
🎮 CS106L Lecture 2 — Streams
🏛 课程:Stanford CS106L — Standard C++ Programming Language
📍 主题:C++ 流(Streams)与输入输出系统
🎯 目标:理解 C++ 的 I/O 抽象模型、字符串流与状态位机制
🧭 课程定位
C++ 中的 流(Stream) 是所有输入输出的统一抽象层。
它隐藏了设备差异,让我们以一致的方式与文件、键盘、网络交互。
“在 C++ 眼中,一切输入输出都是流。”
这堂课教你:
1️⃣ 流的核心概念与层次结构
2️⃣ 如何使用 ostringstream 与 istringstream
3️⃣ 如何检查流状态与避免输入错误
4️⃣ 如何使用 manipulators 控制输出格式
🏷 关键词速记
Stream|流抽象
ostringstream|输出字符串流
istringstream|输入字符串流
good / fail / eof / bad|状态位
flush / endl|缓冲刷新
manipulators|操纵符
🎮 30 秒课程摘要
流是数据的“河流”。
你写入输出流(out),数据顺流而下;
你从输入流(in)读取,数据逆流而上。
一切 I/O 都是流动的概念。
📚 核心内容讲解
🧩 1️⃣ 什么是 Stream?
流(std::istream, std::ostream)是 C++ I/O 的根本抽象。
它屏蔽设备类型:无论来自键盘、文件或字符串,都能以相同方式操作。
📦 输出示例:
std::cout << "Hello, CS106L!" << std::endl;📦 输入示例:
int x;
std::cin >> x;« 与 » 是 流操作符(overloaded operators),
在不同类型上表现不同。
🧠 C++ 的流体系结构:
iostream
├── istream → std::cin, ifstream, istringstream
├── ostream → std::cout, ofstream, ostringstream
└── iostream → 双向流(文件/网络)
🧩 2️⃣ 字符串与流:C-string vs std::string
在现代 C++ 中,不再直接操作 char*。
std::string 可以无缝与流协作。
📦 示例:
std::string name;
std::cout << "Enter your name: ";
std::cin >> name;
std::cout << "Hello, " << name << "!" << std::endl;std::string 自动管理内存与长度,不需手动释放。
🧩 3️⃣ 输出字符串流(ostringstream)
std::ostringstream 将数据写入内存中的字符串缓冲区,而非屏幕。
📦 示例:
std::ostringstream oss;
oss << "Pi is approximately " << 3.14159;
std::string s = oss.str(); // 将流内容取出为 string
std::cout << s; // 输出: Pi is approximately 3.14159
🧩 用途:
- 动态拼接字符串
- 生成日志、格式化输出
- 替代
sprintf(),更安全
🧩 4️⃣ 输入字符串流(istringstream)
std::istringstream 从字符串中读取数据,就像从文件或键盘读一样。
📦 示例:
std::string data = "42 3.14 hello";
std::istringstream iss(data);
int x; double y; std::string word;
iss >> x >> y >> word;
std::cout << x << " | " << y << " | " << word << std::endl;
// 输出: 42 | 3.14 | hello
🧩 用途:
- 从字符串中解析多种类型
- 简化输入格式处理
- 替代手写的字符串切割与转换逻辑
🧩 5️⃣ 状态位(Stream State Flags)
每个输入流都维护一组状态位,表示读取状态:
| 状态 | 含义 |
|---|---|
.good() | 一切正常 |
.fail() | 读取失败(类型不匹配) |
.eof() | 已到文件/输入结尾 |
.bad() | 严重错误(如硬件损坏) |
📦 示例:
std::istringstream iss("abc");
int x;
iss >> x;
if (iss.fail()) std::cout << "Type mismatch!" << std::endl;🧩 使用场景:
- 检查输入是否合法
- 判断是否读完文件
- 清除错误状态
.clear()
🧩 6️⃣ 缓冲与刷新
C++ 的输出流是缓冲的。
std::cout 不一定立即显示输出内容。
| 操作符 | 作用 |
|---|---|
std::endl | 输出换行并刷新缓冲区(性能较慢) |
'\n' | 仅换行,不刷新(更高效) |
std::flush | 手动刷新缓冲区 |
📦 性能对比示例:
for (int i=0; i<100000; ++i)
std::cout << i << '\n'; // ✅ 快
// vs
for (int i=0; i<100000; ++i)
std::cout << i << std::endl; // ⚠️ 慢,频繁刷新
🧩 建议:
平时用 ‘\n’,除非你真的需要立刻看到输出结果。
🧩 7️⃣ 操纵符(Manipulators)
C++ 提供大量操纵符来控制流格式。
| 操纵符 | 功能 |
|---|---|
std::setw(n) | 设置字段宽度 |
std::setprecision(n) | 设置浮点精度 |
std::fixed / std::scientific | 控制浮点输出样式 |
std::boolalpha | 输出 true/false 而非 1/0 |
📦 示例:
std::cout << std::setw(8) << std::setprecision(3)
<< std::fixed << 3.1415926 << std::endl;
// 输出: 3.142
manipulators 是流的“修饰器”,它们链式修改输出行为。
🧠 记忆口诀
“流如水,入出皆通;
写入缓冲,读出如风;
fail 不慌,clear 复用;
endl 慢,\n 才雄。”
🧪 实践练习
✅ 练习 1:字符串拼接日志系统
使用 std::ostringstream 拼接输出:
std::ostringstream log;
log << "[INFO] Frame time = " << 16.7 << " ms";
std::cout << log.str();✅ 练习 2:命令行解析器
从字符串 "move 12 8" 中提取指令与数字参数。
std::istringstream iss("move 12 8");
std::string cmd; int x, y;
iss >> cmd >> x >> y;✅ 练习 3:状态检查
输入错误数据 "abc" 并检测 .fail()。
🪄 创作与工程建议
- 写工具或引擎日志时,优先使用
ostringstream而非字符串拼接。 - 对用户输入要始终检查
.fail()状态。 - 读取配置文件(如
.ini,.cfg)可用istringstream分析每一行。 - 使用
'\n'替代std::endl提升性能。
✅ 小结
| 类别 | 名称 | 用途 |
|---|---|---|
| 输入流 | std::cin, std::ifstream, std::istringstream | 读取 |
| 输出流 | std::cout, std::ofstream, std::ostringstream | 写入 |
| 状态检测 | .good(), .fail(), .eof() | 检查错误 |
| 缓冲控制 | '\n', std::endl, std::flush | 刷新机制 |
“掌握流,就掌握了 C++ 世界的输入与输出之门。”
是否希望我继续为你整理 第 3 讲《Containers》(vector、map、unordered_map)
并保持相同 Notion 风格输出?