title: 【C++】C-中的I/O类总结
tags: C++
description: ’ ’
categories:
- C++
date: 2023-06-05 00:36:59
引入
#include <iostream>
int main(){
std::cout<<"Hello World!"<<std::endl;
}
-
我们在学习C++时,往往都是从上面这段程序开始的
也就是在控制台窗口上打印Hello World -
但是现在回过头去看这段简单的程序
你有没有思考过
cout代表什么?
iostream代表什么?
endl又代表了什么?
<<运算符的作用是什么?
为什么这样的操作就可以在屏幕上打印"Hello World"?
等等问题 -
看似简单的代码,背后却蕴含着庞大的知识体系
-
之前一直对这些问题不太清晰
现在重新回过头来总结一下
概念
C++的标准库中有专门用来进行IO操作的一种类,叫IO类,也叫输入输出流
通过这些IO类可以实现控制台IO,文件IO,内存IO
也就是向控制台/文件/内存
写入数据,
以及从控制台/文件/内存
读取数据,
分类
-
IO一共有9中
-
在iostream.hpp中定义了ostream istream iostream这三个类
(1)istream是用于从控制台读取内容的流类,cin就是该类的对象 (2)ostream是用于把内容输出到控制台的流类,cout就是该类的对象. (3)iostream是既能用于从控制台读取,又能把内容输出到控制台的类。
-
在fstream.hpp中定义了ofstream ifstream fstream这三个类
(1)ifstream是用于从文件读取数据的类. (2)ofstream是用于向文件写人数据的类. (3)fstream是既能从文件读取数据,又能向文件写人数据的类,
-
在sstream.hpp这个头文件中 定义了stringstream类 ostringstream 类 istringstream类
(1)istringstream是用于从内存读取数据的类. (2)ostringstream是用于向内存写人数据的类. (3)stringstream是既能从内存读取数据,又能向内存写入数据的类,
下面两张图展示了不同的IO类之间的继承关系
特性
-
- 不能对IO对象赋值或者拷贝。
ofstream out1,out2;
out1 = out2; //错误:不能对流对象赋值
ofstream print (ofstream); 错误:不能初始化ofstream参数
out2 = print (out2); 错误:不能拷贝流对象
-
2.函数参数和返回值
由1得知 ,当函数的参数或者返回值使用了IO对象类型,不能采用值传递, 不能采用const 引用方式传递 , 只能采用非const 引用方式传递
-
- 错误处理
在通过IO类进行读取的时候 不可避免会出现一些错误,比如文件格式错误 ,输入了错误的格式等等,
C++中定义了一种叫 strm::iostate的bitset来表示对IO类读取操作时的不同状态 包含以下四种状态:1.strm::goodbit:流处于有效状态。该位为 0 表示流没有出现任何错误。 2.strm::failbit:由于格式或类型错误,读写操作失败。 例如,从流中读取的值无法转换为有效的目标类型。该位为 1 表示发生了此类错误。 3.strm::eofbit:已经读到流的末尾,即无法继续读取数据。 该位为 1 表示读取操作已到达流的末尾。 7.strm::badbit:流发生严重的错误,无法恢复。例如,数据无法从磁盘读取或写入磁盘, 或者与底层设备的通信失败。该位为 1 表示发生了此类错误。 ``` 以上四种条件状态可以按位组合使用,例如 s.fail() | s.eof() 表示流可能发生了读取失败或到达末尾的情况。
同时C++的IO类提供了下面这些接口进行状态判定和状态清除
1.s.eof():判断输入流 s 是否读到文件末尾,即文件读取是否结束。如果已经到达文件结尾,就返回 true,否则返回 false。 2.s.fail():判断是否在读取或写入流的过程中出现了错误,例如读取了无效的数据类型。如果流发生了 failbit 或 badbit 错误,则返回 true,否则返回 false。 3.s.bad():判断流是否发生了不可恢复的错误,例如在程序运行时无法打开文件或者无法从流中读取数据。如果流发生了 badbit 错误,则返回 true,否则返回 false。 4.s.good():判断流是否处于有效状态,即没有发生任何错误。如果流处于有效状态,则返回 true,否则返回 false。 5.s.clear():将流 s 的所有条件状态位都复位,将流的状态设置为有效。该函数没有参数,返回值为 void。 6.s.clear(flags):根据给定的 flags 标志位,将流 s 中对应条件状态位复位。flags 的类型为 strm::iostate,可以使用 | 运算符同时设置多个标志位。该函数没有返回值。 7.s.setstate(flags):根据给定的 flags 标志位,将流 s 中对应条件状态位置位。flags 的类型为 strm::iostate,可以使用 | 运算符同时设置多个标志位。该函数没有返回值。 8.s.rdstate():返回流 s 的当前条件状态,
下面是使用实例
-
4.缓冲区机制
什么是缓冲区机制?
当我们在用std::cout的时候 如果这样写的话std::cout<<"Hello World"; 那么当程序执行完这句时 屏幕上可能不会打印Hello world 也可能会打印 这是因为每个输出流都管理一个缓冲区,用来保存程序读写的数据。 当你执行上面的cout时, 内容有可能被操作系统保存在缓冲区中,随后再打印。 也有可能刚好缓冲区满了然后立刻打印
为什么要有缓冲区机制?
有了缓冲机制, 操作系统就可以将程序的多个输出操作组合成单一的系统级写操作。 由于操作系统级的写操作可能很耗时, 允许操作系统将多个输出操作组合 为单一的设备写操作可以带来很大的性能提升。
缓冲区什么时候会刷新?
1.程序正常结束
程序异常结束,输出缓冲区不会被刷新
2.缓冲区满时
3. 使用endl/flush/ends来强制刷新
4.进行unitbuf设置
注意 默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的。
·5.一个输出流可能被关联到另一个流。
在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。
例如,默认情况下,cin和cerr都关联到cout。因此,读cin或写cerr都会导致cout的缓冲区被刷新。