Skip to content

Chapter 9 Streams

Why streams?

早期 C 使用 printf / scanf,C++ 引入 streams,但 C 风格 I/O 仍然可以继续使用。

  • 优点:更好的类型安全、可扩展、更符合面向对象风格
  • 缺点:语法更啰嗦、有时可能更慢(可以关闭同步以提速 cppstd::ios::sync_with_stdio(false);

把 C 代码改成 C++ 时,I/O 不一定要强行重写,原有 stdio 通常可以先保留,只有当需要类型安全、自定义输入输出或统一流接口时,才优先切到 streams。

1 Basic

流(Stream)是面向设备的统一逻辑接口,是一维、单向的,文件流可支持随机访问,但 cin/cout 一般不行。

类型 输入 输出 头文件
Generic istream ostream <iostream>
File ifstream ofstream <fstream>
C string(legacy) istrstream ostrstream <strstream>
C++ string istringstream ostringstream <sstream>
流操作 说明
Extractor(提取器) 从流中读数据,重载 >>
Inserter(插入器) 向流中写数据,重载 <<
Manipulator(流操纵符) 修改流状态,无须重载
  • 文本流(Text streams):处理 ASCII 文本,可能做字符转换(例如换行转换),典型对象包括文件、字符缓冲区
  • 二进制流(Binary streams):处理原始二进制数据,不做文本转换
标准流对象 说明
cin 标准输入
cout 标准输出
cerr 无缓冲错误输出
clog 有缓冲错误输出
示例
#include <iostream>

int i;
float f;
char c;
char buffer[80];

cin >> c;          // 读一个字符
cin >> i;          // 读一个整数,跳过前导空白
cin >> f >> buffer;

2 Input and Extractors

  • 预定义提取器(Predefined extractors)istream >> lvalue,支持多种基础类型与字符串、指针等

  • 默认情况下,提取器通常会跳过前导空白符
  • 流提取运算符:必须是二参数自由函数,第一个参数是 istream&,第二个参数是待写入对象的引用,返回 istream& 以支持链式调用cin >> a >> b >> c; 等价于 ((cin >> a) >> b) >> c;
示例
istream& operator>>(istream& is, T& obj) {
    // 读取 obj
    return is;
}
  • int get():取下一个字符,失败返回 EOF
示例
int ch;
while ((ch = cin.get()) != EOF)
    cout.put(ch);
  • istream& get(char& ch):把下一个字符写入参数
  • get(char* buf, int limit, char delim='\n'):最多读 limit 个字符或到分隔符,会补 '\0'不消费分隔符
  • getline(char* buf, int limit, char delim='\n'):最多读 limit 个字符或到分隔符,会补 '\0'会消费分隔符
  • ignore(int limit=1, int delim=EOF):跳过字符直到计数耗尽或遇到分隔符
  • gcount():返回上一次读取的字符数
  • putback(char c):把一个字符放回流中
  • peek():查看下一个字符但不消耗

3 Output and Inserters

  • 预定义插入器(Predefined inserters)ostream << expression,支持多种基础类型与字符串、指针等
  • 自定义输出运算符:必须是二参数自由函数,第一个参数是 ostream&,第二个参数通常是 const T&,返回 ostream& 以支持链式调用cout << a << b << c; 等价于 ((cout << a) << b) << c;
示例
ostream& operator<<(ostream& os, const T& obj) {
    // 输出 obj
    return os;
}
  • put(char):打印单个字符
示例
cout.put('a');    // 向标准输出流(控制台)输出字符 a
cerr.put('!');    // 向标准错误流输出字符 !
  • flush():强制刷新输出流的缓冲区,让内容立即输出到设备(如控制台),而非暂存于缓冲区
示例
// 先向输出流写入提示语,再通过 flush() 强制刷新,确保提示语立刻显示在屏幕上
cout << "Enter a number";
cout.flush();

4 Manipulators

  • 操纵器(manipulator):用来修改流状态,通常需包含 <iomanip>,很多效果会在后续输出中持续生效
示例
#include <iostream>
#include <iomanip>

int main() {
    cout << setprecision(2) << 1230.243 << endl;
    cout << setw(20) << "OK!";
}

输出为:

1.2e+03
                 OK!

输入时也可搭配操纵器

int n;
cin >> hex >> n; // 后续按十六进制读取

自定义 manipulator
ostream& tab(ostream& out) {
    return out << '\t';
}

cout << "Hello" << tab << "World!" << endl;

Stream Flags

  • 操纵器:setiosflags(flags)resetiosflags(flags)
  • 成员函数:setf(flags)unsetf(flags)