C++IO类,输入输出缓冲区,流状态
我们的程序已经使用了很多IO库设施:
- istream(输入流)类型,提供输入操作。
- ostream(输出流)类型,提供输出操作。
- cin,一个istream对象,从标准输入读取数据。写入到标准错误。
- cout,一个ostream对象,向标准输出写入数据。
- cerr,一个ostream对象,通常用于输出程序错误消息,
- >>运算符,用来从一个istream对象读取输入数据。
- <<运算符,用来向一个ostream对象写入输出数据。
- getline函数(参见3.3.2节,第78页),从一个给定的istream读取一行数据,存入一个给定的string对象中。
IO类
到目前为止,我们已经使用过的IO类型和对象都是操纵char数据的。
默认情况下,这些对象都是关联到用户的控制台窗口的。
当然,我们不能限制实际应用程序仅从控制台窗口进行IO操作,应用程序常常需要读写命名文件。而且,使用IO操作处理string中的字符会很方便。此外,应用程序还可能读写需要宽字符支持的语言。
为了支持这些不同种类的IO处理操作,在istream和ostream之外,标准库还定义了其他一些IO类型,我们之前都已经使用过了。
下表列出了这些类型,分别定义在三个独立的头文件中:
- iostream定义了用于读写流的基本类型,
- fstream定义了读写命名文件的类型,
- sstream定义了读写内存string对象的类型。
头文件 | 类型 |
iostream | istream, wistream从流读取数据 ostream, wostream向流写入数据 iostream, wiostream读写流 |
fstream | ifstream,wifstream从文件读取数据 ofstream, wofstream向文件写入数据 fstream,wfstream读写文件 |
sstream | istringstream,wistringstream从string读取数据 ostringstream, wostringstream向string写入数据 stringstream, wstringstream读写string |
为了支持使用宽字符的语言,标准库定义了一组类型和对象来操纵 wchar_t类型的数据。宽字符版本的类型和函数的名字以一个w开始。
例如,wcin、 woout 和wcerr是分别对应cin、cout 和cerr的宽字符版对象。宽字符版本的类型和对象与其对应的普通char版本的类型定义在同一个头文件中。
例如,头文件fstream定义了ifstream和wifstream类型。
IO类型间的关系
概念上,设备类型和字符大小都不会影响我们要执行的操作。
例如,我们可以用>>读取数据,而不用管是从一个控制台窗口,一个磁盘文件,还是一个string读取。类似的,我们也不用管读取的字符能存入一个char对象内,还是需要一个wchar_t对象来存储。
标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制实现的。
利用模板,我们可以使用具有继承关系的类,而不必了解继承机制如何工作的细节。
类型ifstream和istringstream都继承自istream。
因此,我们可以像使用istream对象一样来使用ifstream和istringstream对象。
也就是说,我们是如何使用cin的,就可以同样地使用这些类型的对象。
例如,可以对一个 ifstream或istringstream对象调用 getline,也可以使用>>从一个ifstream或istringstream对象中读取数据。
类似的,类型ofstream和ostringstream都继承自ostream。因此,我们是如何使用cout的,就可以同样地使用这些类型的对象。
本节剩下部分所介绍的标准库流特性都可以无差别地应用于普通流、文件流和string流,以及char或宽字符流版本。
IO对象无拷贝或赋值
我们不能拷贝或对IO对象赋值:
ofstream outl, out2;
outl = out2; // 错误:不能对流对象赋值
ofstream print (ofstream) ;// 错误:不能初始化ofstream参数
out2 = print (out2); //错误:不能拷贝流对象
由于不能拷贝IO对象,因此我们也不能将形参或返回类型设置为流类型。
进行IO操作的函数通常以引用方式传递和返回流。
读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。
条件状态
IO 操作一个与生俱来的问题是可能发生错误。
一些错误是可恢复的,而其他错误则发生在系统深处,已经超出了应用程序可以修正的范围。
表中列出了IO类所定义的一些函数和标志,可以帮助我们访问和操纵流的条件状态。
strm::iostate | strm 是一种IO类型。iostate是一种机器相关的类型,提供了表达条件状态的完整功能 |
strm::badbit | strm::badbit用来指出流已崩溃 |
strm::failbit | strm::failbit 用来指出一个IO操作失败了 |
stm::eofbit | strm::eofbit用来指出流到达了文件结束 |
stm::goodbit | strm::goodbit用来指出流未处于错误状态。此值保证为零 |
s.eof() | 若流s的eofbit置位,则返回true |
s.fail() | 若流s的failbit或badbit置位,则返回true |
s.bad() | 若流s的badbit置位,则返回true |
s.good() | 若流s处于有效状态,则返回true |
s.clear() | 将流s中所有条件状态位复位,将流的状态设置为有效。返回void |
s.clear(flags) | 根据给定的flags标志位,将流s中对应条件状态位复位。flaqs的类型为strm::iostate。返回void |
s.setstate(flags) | 根据给定的flags标志位,将流s中对应条件状态位置位。flags的类型为strm::iostate。返回void |
s.rdstate() | 返回流s的当前条件状态,返回值类型为strm::iostate |
下面是一个IO错误的例子:
int ival;
cin >> ival;
如果我们在标准输入上键入Boo,读操作就会失败。
代码中的输入运算符期待读取一个int,但却得到了一个字符B。这样,cin会进入错误状态。类似的,如果我们输入一个文件结束标识,cin也会进入错误状态。
一个流一旦发生错误,其上后续的IO操作都会失败。只有当一个流处于无错状态时,我们才可以从它读取数据,向它写入数据。
由于流可能处于错误状态,因此代码通常应该在使用一个流之前检查它是否处于良好状态。
确定一个流对象的状态的最简单的方法是将它当作一个条件来使用:
while (cin >> word)
//ok:读操作成功…
while 循环检查>>表达式返回的流的状态。如果输入操作成功,流保持有效状态,则条件为真。
查询流的状态
将流作为条件使用,只能告诉我们流是否有效,而无法告诉我们具体发生了什么。
有时我们也需要知道流为什么失败。
例如,在键入文件结束标识后我们的应对措施,可能与遇到一个IO设备错误的处理方式是不同的。
IO库定义了一个与机器无关的iostate类型,它提供了表达流状态的完整功能。
这个类型应作为一个位集合来使用。
IO库定义了4个iostate类型的constexpr值,表示特定的位模式。
这些值用来表示特定类型的IO条件,可以与位运算符一起使用来一次性检测或设置多个标志位。
badbit表示系统级错误,如不可恢复的读写错误。通常情况下,一旦badbit被置位,流就无法再使用了。
在发生可恢复错误后,failbit 被置位,如期望读取数值却读出一个字符等错误。这种问题通常是可以修正的,流还可以继续使用。
如果到达文件结束位置,eofbit和failbit都会被置位。goodbit的值为0,表示流未发生错误。
如果badbit、failbit和eofbit任一个被置位,则检测流状态的条件会失败。
标准库还定义了一组函数来查询这些标志位的状态。
操作good在所有错误位均未置位的情况下返回true,而bad、fail和eof则在对应错误位被置位时返回true。
此外,在badbit被置位时,fail也会返回true。这意味着,使用good或fail是确定流的总体状态的正确方法。实际上,我们将流当作条件使用的代码就等价于!fail()。而eof和bad操作只能表示特定的错误。
管理条件状态
流对象的rdstate成员返回一个iostate值,对应流的当前状态。
setstate 操作将给定条件位置位,表示发生了对应错误。
clear成员是一个重载的成员:它有一个不接受参数的版本,而另一个版本接受一个iostate类型的参数。
clear不接受参数的版本清除(复位)所有错误标志位。执行clear()后,调用good会返回true。
我们可以这样使用这些成员:
//记住cin的当前状态
auto old state = cin.rdstate();// 记住 cin的当前状态
cin.clear(); // 使cin有效
process_input (cin); //使用cin
cin.setstate(old state); // 将cin置为原有状态
带参数的clear版本接受一个iostate值,表示流的新状态。
为了复位单一的条件状态位,我们首先用rdstate读出当前条件状态,然后用位操作将所需位复位来生成新的状态。
例如,下面的代码将failbit和badbit复位,但保持eofbit不变:
//复位failbit和badbit,保持其他标志位不变
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);
管理输出缓冲
每个输出流都管理一个缓冲区,用来保存程序读写的数据。
例如,如果执行下面的代码
os << "please enter a value:";
文本串可能立即打印出来,但也有可能被操作系统保存在缓冲区中,随后再打印。
有了缓冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作。
由于设备的写操作可能很耗时,允许操作系统将多个输出操作组合为单一的设备写操作可以带来很大的性能提升。
导致缓冲刷新(即,数据真正写到输出设备或文件)的原因有很多:
- 程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行。
- 缓冲区满时,需要刷新缓冲,而后续的数据才能继续写入缓冲区。
- 我们可以使用操纵符,如endl来显式刷新缓冲区。
- 在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区。默认情况下,对cerr是设置unitbuf的,因此,写到cerr的内容都是立即刷新的
- 一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联的流的缓冲区会被刷新。例如,默认情况下,cin和cerr都被关联到cout,读cin或写cexr都会导致cout的缓冲区被刷新。
刷新输出缓冲区
我们已经使用过操纵符endl,它完成换行并刷新缓冲区的工作。
IO库中还有两个类似的操纵符:flush和ends。
- flush刷新缓冲区,但不输出任何额外的字符;
- ends向缓冲区插入一个空字符,然后刷新缓冲区:
cout << "hi!" << endl; //输出hi和一个换行,然后刷新缓冲区
cout << "hi!" <<flush;//输出hi,然后刷新缓冲区,不附加任何额外字符cout << "hi!" << ends;// 输出hi和一个空字符,然后刷新缓冲区
unitbuf 操纵符
如果想在每次输出操作后都刷新缓冲区,我们可以使用unitbuf操纵符。
它告诉流在接下来的每次写操作之后都进行一次flush操作。
而nounitbuf操纵符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制:
cout << unitbuf; // 所有输出操作后都会立即刷新缓冲区
// 任何输出都立即刷新,无缓冲
cout << nounitbuf; // 回到正常的缓冲方式
警告:如果程序崩溃,输出缓冲区不会被刷新
如果程序异常终止,输出缓冲区是不会被刷新的。当一个程序崩溃后,它所输出的数据很可能停留在输出缓冲区中等待打印。
当调试一个已经崩溃的程序时,需要确认那些你认为已经输出的数据确实已经刷新了。否则,可能将大量时间浪费在追踪代码为什么没有执行上,而实际上代码已经执行了,只是程序崩溃后缓冲区没有被刷新,输出数据被挂起没有打印而已。
关联输入和输出流
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。
标准库将cout和cin关联在一起,
因此下面语句
cin >> ival;
导致cout的缓冲区被刷新。
交互式系统通常应该关联输入流和输出流。这意味着所有输出,包括用户提示信息,都会在读操作之前被打印出来。
tie有两个重载的版本;一个版本不带参数,返回指向输出流的指针。
如果本对象当前未关联到一个输出流,则返回的就是指向这个流的指针,如果对象未关联到流,则返回空指针。
tie的第二个版本接受一个指向ostream的指针,将自己关联到此ostream。即,x.tie(&o)将流x关联到输出流o。
我们既可以将一个istream对象关联到另一个ostream,也可以将一个ostream关联到另一ostream:
cin.tie(&cout); // 仅仅是用来展示:标准库将cin和cout关联在一起// old tie指向当前关联到cin的流(如果有的话)
ostream *old tie = cin.tie(nullptr); // cin不再与其他流关联//将cin与cerr关联;这不是一个好主意,因为cin应该关联到cout
cin.tie(&cerr); // 读取cin会刷新cerr而不是coutcin.tie(old tie); //重建cin和cout间的正常关联
在这段代码中,为了将一个给定的流关联到一个新的输出流,我们将新流的指针传递给了tie。
为了彻底解开流的关联,我们传递了一个空指针。每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream。
相关文章:
C++IO类,输入输出缓冲区,流状态
我们的程序已经使用了很多IO库设施: istream(输入流)类型,提供输入操作。ostream(输出流)类型,提供输出操作。cin,一个istream对象,从标准输入读取数据。写入到标准错误。cout,一个ostream对象,…...

机器学习笔记 - 文字转语音技术路线简述以及相关工具不完全清单
一、TTS技术简述 今天的文本到语音转换技术(TTS)的目标已经不仅仅是让机器说话,而是让它们听起来像不同年龄和性别的人类。通常,TTS 系统合成器的质量是从不同方面进行评估的,包括合成语音的清晰度、自然度和偏好,以及人类感知因素,例如可理解性。 1、技术路线 (1)基…...

阿里云4核8G服务器ECS通用算力型u1实例优惠价格
阿里云4核8G服务器优惠价格955元一年,配置为ECS通用算力型u1实例(ecs.u1-c1m2.xlarge)4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选,CPU采用Intel(R) Xeon(R) Platinum处理器,阿里云活动链接 aliyunfuwuq…...

Jetson nano部署Yolov8 安装Archiconda3+创建pytorch环境(详细教程+错误解决)
由于jetson nano 是aarch64架构,Anaconda官方不支持aarch64架构,所以有了一个叫“Archiconda”,其目的就是将conda移植到aarch64平台上 一. 下载地址Releases Archiconda/build-tools GitHub 然后安装archiconda bash Archiconda3-0.2.3…...

Node.JS多线程PromisePool之promise-pool库实现
什么是Promise Pool Map-like, concurrent promise processing for Node.js. Promise-Pool是一个用于管理并发请求的JavaScript库,它可以限制同时进行的请求数量,以避免过多的请求导致服务器压力过大。使用Promise-Pool可以方便地实现对多个异步操作的并…...

【C++】红黑树讲解及实现
前言: AVL树与红黑树相似,都是一种平衡二叉搜索树,但是AVL树的平衡要求太严格,如果要对AVL树做一些结构修改的操作性能会非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更…...
security如何不拦截websocket
只要添加一个关键配置就行 //忽略websocket拦截Overridepublic void configure(WebSecurity webSecurity){webSecurity.ignoring().antMatchers("/**");} 全部代码我放着了 package com.oddfar.campus.framework.config;import com.oddfar.campus.framework.secur…...

Unity类银河恶魔城学习记录12-3 p125 Limit Inventory Slots源代码
Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Inventory.cs using Newtonsoft.Json.Linq; using System.Collections; us…...

【智能排班系统】雪花算法生成分布式ID
文章目录 雪花算法介绍起源与命名基本原理与结构优势与特点应用场景 代码实现代码结构自定义机器标识RandomWorkIdChooseLocalRedisWorkIdChooselua脚本 实体类SnowflakeIdInfoWorkCenterInfo 雪花算法类配置类雪花算法工具类 说明 雪花算法介绍 在复杂而庞大的分布式系统中&a…...
sass中的导入与部分导入
文章目录 sass中的导入与部分导入1. import:传统的导入方式2. use:现代化的模块化导入 sass中的导入与部分导入 在大型前端项目中,CSS代码量往往十分庞大,为了保持其可读性、可维护性以及便于团队协作,模块化开发成为…...

工业组态 物联网组态 组态编辑器 web组态 组态插件 编辑器
体验地址:by组态[web组态插件] BY组态是一款非常优秀的纯前端的【web组态插件工具】,可无缝嵌入到vue项目,react项目等,由于是原生js开发,对于前端的集成没有框架的限制。同时由于BY组态只是一个插件,不能独…...

git可视化工具
Gitkraken GitKraken 是一款专门用于管理和协作Git仓库的图形化界面工具。它拥有友好直观的界面,使得Git的操作变得更加简单易用,尤其适合那些不熟悉Git命令行的开发者。GitKraken提供了丰富的功能,如代码审查、分支管理、仓库克隆、提交、推…...

基于单片机电子密码锁系统设计
**单片机设计介绍,基于单片机电子密码锁系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机电子密码锁系统设计概要主要包括以下几个方面: 一、系统概述 基于单片机电子密码锁系统是一个…...
点云从入门到精通技术详解100篇-基于点云与图像纹理的 道路识别(续)
目录 3.1.2 图像滤波去噪 3.2 道路纹理特征提取 3.3 基于超像素分割的图像特征表达...
《机器学习在量化投资中的应用研究》目录
机器学习在量化投资中的应用研究 获取链接:机器学习在量化投资中的应用研究_汤凌冰著_北京:电子工业出版社 更多技术书籍:技术书籍分享,前端、后端、大数据、AI、人工智能... 内容简介 《机器学习在量化投资中的应用研究…...

Spring拓展点之SmartLifecycle如何感知容器启动和关闭
Spring为我们提供了拓展点感知容器的启动与关闭,从而使我们可以在容器启动或者关闭之时进行定制的操作。Spring提供了Lifecycle上层接口,这个接口只有两个方法start和stop两个方法,但是这个接口并不是直接提供给开发者做拓展点,而…...

深入理解Java匿名内部类(day21)
在Java编程中,匿名内部类是一种非常有用的特性,它允许我们定义和实例化一个类的子类或实现一个接口,而无需给出子类的名称。这种特性使得代码更加简洁、紧凑,尤其适用于一些只使用一次的临时对象。本文将深入探讨Java匿名内部类的…...
《状态模式(极简c++)》
本文章属于专栏- 概述 - 《设计模式(极简c版)》-CSDN博客 模式说明: 方案:状态模式是一种行为设计模式,用于在对象的内部状态发生改变时改变其行为。它包括三个关键角色:上下文(Context&#x…...
Day4-Hive直播行业基础笔试题
Hive笔试题实战 短视频 题目一:计算各个视频的平均完播率 有用户-视频互动表tb_user_video_log: id uid video_id start_time end_time if_follow if_like if_retweet comment_id 1 101 2001 2021-10-01 10:00:00 2021-10-01 10:00:30 …...
mybatis批量新增数据
数据量大的时候如果在循环中执行单条新增操作,是非常慢的。那么如何在mybatis中实现批量新增数据呢? 方法 insert 标签的 foreach 属性可以用于批量插入数据。您可以使用 foreach 属性遍历一个集合,并为集合中的每个元素生成一条插入语句。…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...