跳转至

Streams⚓︎

763 个字 91 行代码 预计阅读时间 5 分钟

(stream) 是一种为设备提供的公共逻辑接口,具备顺序 (sequential) 的特征,能够生产或消耗值。

它的优缺点是:

  • 优点:提供了更好的类型安全,具备可扩展性,体现更多面向对象的特征
  • 缺点:代码更加冗长,执行速度更慢

流的名称:

输入 输出 头文件
泛型 istream ostream <iostream>
文件 ifstream ofstream <fstream>
字符串 istringstream ostringstream <sstream>

流运算:

  • 提取符(extractor):
    • 从流中读取值
    • 重载运算符 >>
  • 插入符(insertor):
    • 将值插入到流中
    • 重载运算符 <<
  • 操纵符(manipulator):改变流的状态
  • 其他 ...

流的种类:

  • 文本流
    • 用于处理 ASCII 文本
    • 以行的形式组织
    • 执行一些字符转换,比如将新的行转换为真正的 OS 文件
    • 包括:文件、字符缓冲区等
  • 二进制流
    • 用于处理二进制数据
    • 没有转换

预定义的流:

  • cin:标准输入
  • cout:标准输出
  • cerr:未缓存的错误(调试)输出
  • clog:缓存的错误(调试)输出

预定义的提取符 istream >> lvalue 会忽视前导空白字符,它的定义如下:

istream& operator>>(istream& is, T& obj) {
    // specific code to read obj
    return is;
}

其他的输入运算符:

  • int get():返回流里面的下一个字符,如果流里没有字符的话则返回 EOF

    int ch;
    while ((ch = cin.get()) != EOF)
        cout.put(ch);
    
  • istream& get(char &ch):讲下一个字符放到参数里,功能和上一个输入运算符类似

  • istream& getline(istream& is, string& str, char delim = '\n');:自由函数?
  • ignore(int limit = 1, int delim = EOF):跳过 limit 个字符以及分隔符
  • int gcount():返回刚刚读取的字符数

    string buffer;
    getline(cin, buffer);
    cout << "read " << cin.gcount() << " characters"
    
  • void putback(char):将单个字符推回到流里面

  • char peek():检查下一个字符,但不读取它

预定义的插入符 ostream << expression,它的定义如下:

ostream& operator<<(ostream& os, const T& obj) {
    // specific code to write obj
    return os;
}

其他的输出运算符:

  • put(char):打印单个字符

    cout.put('a');
    cerr.put('!');
    
  • flush():强制输出流的内容

    cout << "Enter a number";
    cout.flush();
    

使用操纵符进行格式化,修改流的状态

  • 导入头文件:#include<iomanip>
  • 各种操纵符:
操纵符 效果 类型
dec, hex, oct 设置数值转换 I, O
endl 插入新行并清除流的内容 O
flush 清除流的内容 O
setw(int) 设置字段宽度 I, O
setfill(char) 改变填充字符 I, O
setbase(int) 设置数字的基底 O
ws 跳过空白字符 I
setprecision(int) 设置浮点数精度 O
例子
#include <iostream>
#include <iomanip>
int main() {
    cout << setprecision(2) << 1000.243 <<endl;
    cout << setw(20) << "OK!";
    return 0;
}

运行结果:

1e03
                OK!

我们可以自定义操纵符:

// skeleton for an output stream manipulator
ostream& manip(ostream& out) {
    ...
    return out;
}
ostream& tab ( ostream& out ) {
    return out << '\t';
}
cout << "Hello" << tab << "World!" << endl;

流标志控制格式:

标志 目的(何时设置)
ios::skipws 跳过前导空白字符
ios::left, ios::right 对齐
ios::internal 在符号和值之间填充
ios::dec, ios::hex, ios::oct 数字格式
ios::showbase 显示数字基底
ios::showpoint 始终显示小数点
ios::uppercase 让基底以大写形式呈现
ios::showpos 显示正数的 +

设置标志:

  • 使用操纵符:setiosflags(flags), resetiosflags(flags)
  • 使用 stream 成员变量:setf(flags), unsetf(flags)
例子
#include <iostream>
#include <iomanip>
int main() {
    cout setf(ios::showpos | ios::scientific);
    cout << 123 << " " << 456.78 << endl;
    cout << resetiosflags(ios::showpos) << 123;
    return 0;
}

运行结果:

+123 +4.567800e+02
123

流的错误状态:

  • 每次操作后都会设置错误状态
  • clear() 用于重置错误状态为 good()
  • 状态检查:
    • good():当状态有效时返回 true
    • eof():当遇到 EOF 时返回 true
    • fail():当出现轻微故障或不良状态时返回 true
    • bad():当状态不良时返回 true
例子
int n;
cout << "Enter a value for n, then [Enter]" << flush;
while (cin.good()) {
    cin >> n;
    if (cin) { // input was ok
        cin.ignore(INT_MAX, '\n'); // flush newline
        break;
    }
    if (cin.fail()) {
        cin.clear(); // clear the error state
        cin.ignore(INT_MAX, '\n'); // skip garbage
        cout << "No good, try again!" << flush;
    }
}

文件流

  • ifstreamofstream 将文件和流连接起来
  • 需要导入头文件:#include <fstream>
  • 用模式指明如何创建文件:
模式 目的
ios:app 附加
ios:ate 末尾位置
ios:binary 处理二进制 I/O
ios:in 为输入打开文件
ios:out 为输出打开文件
例子
#include <iostream>
#include <fstream>
int main(int argc, char *argv[]) {
    if (argc != 3) {
        cerr << "Usage: copy file1 file2" << endl;
        exit(1);
    }
    ifstream in(argv[1]);
    if (!in) {
        exit(2);
        cerr << "Unable to open file " << argv[1];
    }
    ofstream out(argv[2]);
    if (!out) {
        exit(2);
        cerr << "Unable to open file " << argv[2];
    }
    char c;
    while (in >> c) {
        out << c;
    }
}

相关的流操作:

  • open(const char *, int flags, int):打开指定文件

    ifstream inputS;
    inputS.open("somefile", ios::in);
    if (!inputS) {
        cerr << "Unable to open somefile";
        ...
    
  • close() 关闭流


I/O 流缓冲区:

  • 每个 I/O 都有一个流缓冲区
  • streambuf 定义了这个缓冲区的抽象
  • 成员函数 rdbuf() 返回指向流缓冲区的指针
  • << 被重载,用于直接连接缓冲区
例子
#include <fstream>
#include <assert>
int main(int argc, char *argv[]) {
    assert(argc == 2);
    ifstream in(argv[1]);
    assert(in); // check that stream opened
    cout << in.rdbuf(); // Drain file!
}

评论区

如果大家有什么问题或想法,欢迎在下方留言~