C++ Primer 库-IO类

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 8.1 IO类
    • IO类型间的关系
    • IO对象无拷贝或赋值
      • 条件状态
    • 查询流的状态
    • 管理条件状态
    • 管理输出缓冲
    • 刷新输出缓冲区
    • unitbuf操纵符
    • 关联输入和输出流

8.1 IO类

到目前为止,我们已经使用过的IO类型和对象都是操纵char数据的。默认情况下,这些对象都是关联到用户的控制台窗口的。当然,我们不能限制实际应用程序仅从控制台窗口进行IO操作,应用程序常常需要读写命名文件。而且,使用IO操作处理string中的字符会很方便。此外,应用程序还可能读写需要宽字符支持的语言。

为了支持这些不同种类的IO处理操作,在istream和ostream之外,标准库还定义了其他一些IO类型,我们之前都已经使用过了。列出了这些类型,分别定义在三个独立的头文件中: tostream定义了用于读写流的基本类型,fstream定义了读写命名文件的类型,sstream定义了读写内存string对象的类型。

表8.1 IO库类型和头文件

头文件类型
ostreamstream,wistream从流读取数据
ostream,wostream向流写入数据
tostream,wiostream读写流
fstreamifstream,wifstream从文件读取数据
ofstream,wofstream向文件写入数据
fstream,wfstream读写文件
streamistringstream,wistringstream从string读取数据
ostringstream,wostringstzeam向string写入数据
stringstream,wstringstream读写string

为了支持使用宽字符的语言,标准库定义了一组类型和对象来操纵wchar_t类型的数据。宽字符版本的类型和函数的名字以一个w开始。例如,的类型和对象与其对应的普通char版本的类型定义在同一个头文件中。例如,头文件fstream定义了ifstream

IO类型间的关系

概念上,设备类型和字符大小都不会影响我们要执行的IO操作。例如,我们可以用>>读取数据,而不用管是从一个控制台窗口,一个碟盘文件,还是一个string读取。类似的,我们也不用管读取的字符能存入一个char对象内,还是需要一个wchar_t对象来存储。

标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制(inheritance)实现的。利用模板,我们可以使用具有继承关系的类,而不必了解继承机制如何工作的细节。

简单地说,继承机制使我们可以声明一个特定的类继承自另一个类。我们通常可以将一个派生类(继承类)对象当作其基类(所继承的类)对象来使用。

类型ifstream和istringstream都继承自istream。因此,我们可以像使用istream对象一样来使用i1fstream和istringstream对象。也就是说,我们是如何使用cin的,就可以同样地使用这些类型的对象。例如,可以对一个ifstream或istringstream对象调用getline,也可以使用>>从一个ifstream或istringstream对象中读取数据。类似的,类型ofstream和ostringstream都维承自ostream。因此,我们是如何使用cout的,就可以同样地使用这些类型的对象。

IO对象无拷贝或赋值

如我们在所见,我们不能拷贝或对IO对象赋值:

ofstream out1,out2;
out1 = out2;               //错误:不能对流对象赋值
ofstream print(ofstream);  //错误:不能初始化ofstream参数
out2=print(out2);          //错误:不能拷贝流对象

由于不能拷贝IO对象,因此我们也不能将形参或返回类型设置为流类型。进行IO操作的函数通常以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。

条件状态

IO操作一个与生俱来的问题是可能发生错误。一些错误是可恢复的,而其他错误则发生在系统深处,已经超出了应用程序可以修正的范围。表8.2列出了IO类所定义的一些函数和标志,可以帮助我们访问和操纵流的条件状态(condition state)。

表8.2:IO库条件状态

std::iostatestrm是一种IO类型,在表8.1中已列出。iostate 是一种机器相关的类型,提供了表达条件状态的完整功能
strm::badbitsstrm::badbit用来指出流已崩溃
strm::failbitstrm::failbit用来指出一个IO操作失败了
strm::eofbitsstrm::eofbit用来指出流到达了文件结束
strm::goodbitsstrm::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(f1ags)根据给定的flags标志位,将流s中对应条件状态位复位。flags的类型为strm::tostate。返void
s.setstate(f1ags)根据给定的flags标志位,将流s中对应条件状态位置位。f1ags的类型为swm::tostate。返回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条件,可以与位运算符一起使用来一次性检测或设置多个标志位。

badbit表示系统级错误,如不可恢复的读写错误。通常情况下,一旦badbit被置位,流就无法再使用了。在发生可恢复错误后,failbit被置位,如期望读取数值却读出一个字符等错误。这种问题通常是可以修正的,流还可以继续使用。如果到达文件结束位置,eofbit和failbit都会被置位。goodbit的值为0,表示流未发生错误。如果badbit、failbit和eofbit任一个被置位,则检测流状态的条件会失败。

标准库还定义了一组函数来查询这些标志位的状态。操作good在所有错误位均未置位的情况下返回true,而bad、fail和eof则在对应错误位被置位时返回true。此外,在badbit被置位时,fail也会返回true。这意味着,使用good或fai1是确定流的总体状态的正确方法。实际上,我们将流当作条件使用的代码就等价于!fail()。而ecf和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(ctn.rdstate()~cin.failbit&~cin.badbit);

管理输出缓冲

每个输出流都管理一个缓冲区,用来保存程序读写的数据。例如,如果执行下面的代码

os<<"please enter a value:";

文本串可能立即打印出来,但也有可能被操作系统保存在缓冲区中,随后再打印。有了缄冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作。由于设备的写操作可能很耗时,允许操作系统将多个输出操作组合为单一的设备写操作可以带来很大的性能提升。

导致缓冲刷新(即,数据真正写到输出设备或文件)的原因有很多:

  • 程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行。
  • 缓视区满时,需要刷新缓冲,而后新的数据才能继续写入缓冲区。
  • 我们可以使用操纵符如endl来显式刷新缓冲区。
  • 在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区。默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的。
  • 一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刹新。例如,默认情况下,cin和cerr都关联到cout。因此,读cin或写cerr都会导致cout的缓冲区被刷新。

刷新输出缓冲区

我们已经使用过操纵符endl,它完成换行并刷新缓冲区的工作。IO库中还有两个类似的操纵符:flush和ends。flush刷新缓冲区,但不输出任何额外的字符;ends向缓沥区插入一个空字符,然后刷新缓冲区:

cout<<"hi!"<<endl;//输出hi和一个换行,然后刷新缓冲区
cout<<"hi!"<<flush;//输出hi,然后刷新缓冲区,不附加任何额外字符
cout<<"hi!"<<endl;//输出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(nulLlptr);//cin不再与其他流关联
//将cin与cerr关联;这不是一个好主意,囚为cin应该关联到cout
cin.tie(&cerr);//读取cin会刷新cerr而不是cout
cin.tie(old_tie)}//重建cin和cout间的正常关联

在这段代码中,为了将一个给定的流关联到一个新的输出流,我们将新流的指针传递给了tie。为了彻底解开流的关联,我们传递了一个空指针。每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/973306.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FlutterAssetsGenerator插件的使用

在Plugins中找到FlutterAssetsGenerator插件&#xff0c;点击安装。 更改生成的资源索引类可以修改名字。 在根目录下创建assets/images文件夹&#xff0c;用于存储图片。 点击images文件夹&#xff0c;鼠标右键点击Flutter&#xff1a;Configuring Paths&#xff0c;pub…

网络安全与防范

1.重要性 随着互联网的发达&#xff0c;各种WEB应用也变得越来越复杂&#xff0c;满足了用户的各种需求&#xff0c;但是随之而来的就是各种网络安全的问题。了解常见的前端形式和保护我们的网站不受干扰是我们每个优秀fronter必备的技能。 2.分类 XSS干扰 CSRF干扰 网络劫持干…

【算法】----多重背包问题I,II(动态规划)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

超低失真、超高清晰度的远心工业镜头

随着机器视觉技术的不断提高&#xff0c;工业生产应用机器视觉系统也越来越广泛&#xff0c;大大提高的工厂的效率&#xff0c;而有时候采集的图像有些扭曲变形&#xff0c;也就是失真&#xff0c;图像失真又叫“畸变”&#xff0c;远心镜头就是为纠正传统工业镜头视差而设计一…

JWT认证机制

Session认证机制中需要配合cookie才能实现&#xff0c;由于cookie默认不支持跨域访问&#xff0c;当涉及到前端跨域请求后端接口时&#xff0c;需要做很多额外的配置&#xff0c;才能实现跨域session认证。所以这里不推荐使用session身份认证机制&#xff0c;一般推荐使用jwt认…

嵌入式八股文(四)计算机网络篇

第一章 基础概念 1. 服务 指网络中各层为紧邻的上层提供的功能调用,是垂直的。包括面向连接服务、无连接服务、可靠服务、不可靠服务。 2. 协议 是计算机⽹络相互通信的对等层实体之间交换信息时必须遵守的规则或约定的集合。⽹络协议的三个基本要素:语法、…

数据结构——单向循环链表、双链表、双向循环链表

目录 一、单向循环链表 1.1 单向循环链表的概念 1.2 单向循环链表的操作 1.2.1 单向循环链表的创建 1.2.2 单向循环链表的头插 1.2.3 单向循环链表的遍历 1.2.4 单向循环链表的头删 1.2.5 单向循环链表的尾插 1.2.6 单向循环链表的尾删 1.2.7 约瑟夫环 1.3 单向循环列表所有程…

dify安装

官网教程 https://github.com/langgenius/dify/blob/main/README_CN.md 1、下载源码 git clone https://github.com/langgenius/dify.git 2、进入docker目录 cd dify cd docker cp .env.example .env修改nginx对外端口配置 修改为9000 最后执行&#xff1a;docker compo…

使用Termux将安卓手机变成随身AI服务器(page assist连接)

通过以下方法在安卓手机上运行 Ollama 及大模型&#xff0c;无需 Root 权限&#xff0c;具体方案如下&#xff1a; 通过 Termux 模拟 Linux 环境运行 核心工具&#xff1a; 安装 &#xff08;安卓终端模拟器&#xff09;()]。借助 proot-distro 工具安装 Linux 发行版&#xf…

C++ STL中的reverse/unique/sort/lower_bound/upper_bound函数使用

本文主要涉及以下几个函数&#xff1a; reverse&#xff1a;反转序列。unique&#xff1a;移除相邻重复元素。sort&#xff1a;对序列进行排序。lower_bound 和 upper_bound&#xff1a;查找目标值的边界位置。头文件均为<algorithm> 1. reverse 功能&#xff1a;反转指…

QT QLabel加载图片等比全屏自适应屏幕大小显示

最近在工作项目中,遇到一个需求: 1.使用QLabel显示一张图片; 2.当点击这个QLabel时,需要全屏显示;但不能改变原来的尺寸; 3.当点击放大后的QLabel时,恢复原有大小. 于是乎,就有了本篇博客,介绍如何实现这样的功能. 一、演示效果 在一个水平布局中&#xff0c;添加两个Lable用…

C# 背景 透明 抗锯齿 (效果完美)

主要是通过 P/Invoke 技术调用 Windows API 函数 gdi32.dll/user32.dll&#xff0c;同时定义了一些结构体来配合这些 API 函数的使用&#xff0c;常用于处理图形绘制、窗口显示等操作。 运行查看效果 局部放大&#xff0c;抗锯齿效果很不错,尾巴毛毛清晰可见。 using System; u…

Windows10 将Docker虚拟磁盘文件ext4.vhdx迁移至D盘

今天打开电脑发现之前迁移到D盘的ext4.vdx居然占有80多个G不得不重新清理一下了 于是先删除了d盘的ext4.vdx文件 注销了原来的 wsl --unregister docker-desktopwsl --unregister docker-desktop-data 确认 WSL 发行版状态&#xff1a; 运行以下命令以确认当前的 WSL 发行版…

OpenCV二值化处理

1.1. 为什么需要二值化操作 二值化操作将灰度图像转换为黑白图像&#xff0c;即将图像中的像素值分为两类&#xff1a;前景&#xff08;通常为白色&#xff0c;值为 255&#xff09;和背景&#xff08;通常为黑色&#xff0c;值为 0&#xff09;。二值化的主要目的是简化图像&…

深入了解 DevOps 基础架构:可追溯性的关键作用

在当今竞争激烈的软件环境中&#xff0c;快速交付强大的应用程序至关重要。尽管如此&#xff0c;在不影响质量的情况下保持速度可能是一项艰巨的任务&#xff0c;这就是 DevOps 中的可追溯性发挥作用的地方。通过提供软件开发生命周期 &#xff08;SDLC&#xff09; 的透明视图…

由浅入深学习大语言模型RLHF(PPO强化学习- v1浅浅的)

最近&#xff0c;随着DeepSeek的爆火&#xff0c;GRPO也走进了视野中。为了更好的学习GRPO&#xff0c;需要对PPO的强化学习有一个深入的理解&#xff0c;那么写一篇文章加深理解吧。纵观网上的文章&#xff0c;要么说PPO原理&#xff0c;各种复杂的公式看了就晕&#xff0c;要…

【Java八股文】08-计算机网络面试篇

【Java八股文】08-计算机网络面试篇 计算机网络面试篇网络模型网络OSI模型和TCP/IP模型分别介绍一下键入网址到网页显示&#xff0c;期间发生了什么&#xff1f; 应用层- HTTP应用层有哪些协议&#xff1f;HTTP是什么及HTTP报文有哪些部分&#xff1f;HTTP是怎么传输数据的HTTP…

【Linux】Linux 文件系统——有关 inode 不足的案例

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天周二了&#xff0c;明天星期三&#xff0c;还有三天就是星期五了&#xff0c;坚持住啊各位&#xff01;&#xff01;&#xff01;&#x1f606; 本文是对之前Linux文件权限中的inode号进行实例讨论&#xff0c;看到博客有错误…

SpringBoot整合Redis和Redision锁

参考文章 1.Redis 1.导入依赖 <!--Redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.c…

亲测可用,IDEA中使用满血版DeepSeek R1!支持深度思考!免费!免配置!

作者&#xff1a;程序员 Hollis 之前介绍过在IDEA中使用DeepSeek的方案&#xff0c;但是很多人表示还是用的不够爽&#xff0c;比如用CodeChat的方案&#xff0c;只支持V3版本&#xff0c;不支持带推理的R1。想要配置R1的话有特别的麻烦。 那么&#xff0c;今天&#xff0c;给…