OOP Lec15: Stream

Stream

C vs. C++

C

在 C 语言中,I/O 使用 scanf()printf()

在运行时解析格式字符串,和给定的地址交互。

通常处于更底层、更快。

最底层的函数 fread()read(),需要手写快速输入时使用。

缺点:几乎没有类型检查,没有面向对象的重载方法

C++

相比之下,C++ 的 stream 有更好的类型检查可拓展性面向对象风格

缺点:通常更慢

C + C++

当一个 C++ 程序中使用了 C 风格的输入输出时,请不要再使用 C++ 风格的输入输出。

原因:若两种风格的输入输出不共用缓冲区,混用会导致输出的顺序和得到的结果的顺序不同。

声明

位置

交互的对象 头文件 输入类名 输出类名
通用(控制台) <iostream> istream ostream
文件 <fstream> ifstream ofstream
字符串 string <sstream> istringstream ostringstream

种类

  • 输入类
    • 从交互处读取数据,通过输入类传递给对象
    • 重载了 >> 运算符
  • 输出类
    • 从需要输出的对象读取数据,传递给交互处
    • 重载了 << 运算符
  • 控制符
    • 声明在 <iomanip>
    • 用于控制输入输出的格式

文本流与二进制流

文本流指的是 ASCII、Unicode 或其他文本编码构成的字符数据流,文本流是特殊的二进制流。

以二进制方式读写文本流是允许的,反过来则不行。

文本流和二进制流的区别:

  1. 文本流的内容均为可读字符
  2. 文本流以行为单位
  3. 读取文本流的时候,内容可能被自动识别并修改(\n 可能是 0D MacOS、0A Unix 或 0D0A Windows)

使用

预定义的 stream

  • cin:标准输入,绑定到 stdin
  • cout:标准输出,绑定到 stdout
  • cerr:无缓冲的调试区输出,绑定到 stderr
  • clog:有缓冲的调试区输出,绑定到 stderr
#include <iostream>
#include <string>
int main(){
    int i;
    float f;
    char c;
    std::string str;
    std::cin >> str;
    std::cin >> i >> f;
    std::cout << str << ": " << i << " + " << f << '\n';
    std::cerr << "Byebye\n"; 
    return 0;
}
>>> Hello 4 3
<<< Hello: 4 + 3
<<< Byebye

如果在 Linux 下启动,还可以重定向:

./test 1>output 2>err

那么你可以分别在 output 和 err 文件中看见输出。

上文的 1 可以省略,它表示 stdout;2 表示 stderr;0 表示 stdin,且使用 < 而非 >

预定义流的函数

cin

  • int get():返回下一个字节,或者 EOF

  • istream& get(char& ch):读取下一个字节 cin >> get(ch);

  • istream& getline(istream& is, string& str, char delim = '\n'):读取一行。

    • 是自由函数
    • 遇到 delimiter 停止
  • istream& getline(char* str, int size):读取接下来 size 个字符。

    • istream 的成员
    • 不推荐使用
  • void ignore(int limit = 1, int delim = EOF):忽略接下来 limit 个字符。

    • istream 的成员
    • 如果遇到 delimiter,将其忽略,然后停止
  • int gcount():返回上一个操作读取字符的数量。

  • void putback(char ch):将一个字符放回流中。

  • char peek():返回下一个字符,但不读取它。

cout

  • void put(char ch):将一个字符放入缓冲区。
  • void flush():刷新缓冲区,将缓冲区中的字符全部打印。

对类重载

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

运算符的返回值一定是 istream&,这样才能允许连续读入。

格式化

使用控制符来格式化,代替格式字符串的格式控制功能。

std::cin >> std::hex >> n;

那么 n 输入的值应当被视为 16 进制,例如输入 12 会被视为 16 + 2 = 18。

通常控制符的效果是持续的,持续到下一次同类控制符生效。

预定义的控制符

控制符 控制流的类型 效果
ws I 跳过空格
setw(int) I/O 指定字段所占的宽度
setfill(char) I/O 指定占位符
hexdecoct I/O 指定输出的进制
setbase(int) O 指定输出的进制
setprecision(int) O 指定浮点数的输出精度
endl O 换行,并刷新缓冲区
flush O 刷新缓冲区

自定义控制符

ostream& tab(ostream& os){
    // ... any code
    os << '\t';
    return os;
}

这样就可以通过 os << tab 来使用控制符了。

格式标志

std::ios 空间下有一些格式标志,用于设置流的格式。

例如当设置了 std::ios::showpos 后,非负数在输出时会带上 +

设置方法:

  • setf(flags)unsetf(flags):stream 的成员函数
  • setiosflags(flags)resetiosflags(flags):控制符
标志 意义
skipws 跳过前导空格
leftright 左右对齐
scientific 使用科学计数法表示浮点数
fixed 使用正常的计数方法表示浮点数
hexdecoct 数值的进制
showbase 显示数值的进制
showpoint 总是显示小数点
showpos 在非负数前显示 +
uppercase 大写显示科学计数法的 E 和 16 进制的 X
internal 在符号和数值之间插入字符

实际上,流内部的状态用一个 32 位二进制表示,每一个标志对应 32 位二进制上的一位,该位为 1 表示标志有效,0 表示无效。

所以 setf() 时可以将不同的标志或起来。

#include <iostream>
#include <iomanip>
using namespace std;
int main(){
    cout.setf(ios::showpos | ios::scientific);
    cout << 123 << " " << 456.78 << endl;
    cout << resetiosflags(ios::showpos) << 123 << endl;
    return 0;
}

状态机

cin 的四个状态:

  • good:正常工作
  • bad:读取内容时出错,可能是网络断开、文件无法打开、文件损坏等
  • fail:操作错误,想读取一个整数,结果下一段内容是字符串
  • eof:End of File,表示文件内容读取完毕

cin.clear():将 cin 重置为 good。

注意重置之后并不改变流内的内容。如果需要重新输入,请配合 ignore() 使用。

cin.good() cin.bad() cin.fail() cin.eof():返回一个布尔值,表示 cin 是否为该状态。

cin 重载了operator int()cin.good(),所以 if(cin) 等价于 if(cin.good())

fstream

打开模式

在 C 风格的文件 I/O 中,我们使用字符串 w+rb 等来指定文件打开的模式;

对于 ifstream 和 ofstream,我们使用标志来完成这个任务。

和前面的标志类似,这些标志声明在 std::ios 中,可以或运算。

模式 意义
app 每次输出都在文件末尾追加
ate 在文件末尾打开
binary 二进制输入输出
in 读取文件
out 写入文件
trunc 打开前丢弃流的内容
noreplace (C++23) 如果已经存在文件,抛出异常

使用

简单情况下,我们可以直接使用构造函数:

ifstream fin1("id.txt"); // default ios::in
ifstream fin2("key.txt", ios::binary | ios::in);
ofstream fout("name.txt", ios::app | ios::out); // default ios::out

当然我们可以使用两步构造。第二步,打开文件的成员是 open(const char*, int flags)

ifstream fin;
fin.open("key.txt", ios::binary | ios::in);
ofstream fout;
fout.open("name.txt", ios::app | ios::out);

关闭文件流使用 close() 即可:

fout.close();

更多内容:std::basic_fstream - cppreference.com

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇