C++标准库--IO库(Primer C++ 第五版 · 阅读笔记)

news2025/1/19 3:16:06

C++标准库--IO库(Primer C++ 第五版 · 阅读笔记)

  • 第8章 IO库
    • 8.1、IO类
    • 8.2、文件输入输出
    • 8.3、string流
      • 总结:

第8章 IO库

8.1、IO类

为了支持这些不同种类的IO处理操作,在istreamostream之外,标准库还定义了其他一些IO类型。

  • 如下图分别定义在三个独立的头文件中: iostream定义了用于读写的基本类型,fstream定义了读写命名文件的类型,sstream定义了读写内存string对象的类型。
  • 为了支持使用宽字符的语言,标准库定义了一组类型和对象来操纵wchar_t类型的数据。宽字符版本的类型和函数的名字以一个w开始,例如:wcinwcoutwcerr是分别对应cincoutcerr的宽字符版对象。
  • 宽字符版本的类型和对象与其对应的普通char版本的类型定义在同一个头文件中。

在这里插入图片描述

类型ifstreamistringstream都继承自istream。因此,我们可以像使用istream对象一样来使用ifstreamistringstream对象。
也就是说,我们是如何使用cin 的,就可以同样地使用这些类型的对象。例如,可以对一个 ifstreamistringstream对象调用getline ,也可以使用>>从一个 ifstreamistringstream对象中读取数据。

  1. IO对象无拷贝或赋值
ofstream out1, out2;
out1 = out2;					//错误:不能对流对象赋值
ofstream print(ofstream);		//错误:不能初始化ofstream参数
out2 = print(out2);				//错误:不能烤贝流对象
  1. 条件状态

在这里插入图片描述
在这里插入图片描述
我们可以这样使用这些成员:

//记住cin的当前状态
auto old_state = cin.rdstate(); 		//记住cin的当前状态
cin.clear();							//使cin有效
process_input(cin);						//使用cin
cin.setstate(old_state);				//将cin置为原有状态

//复位failbit和badbit,保持其他标志位不变
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);
  1. 管理输出缓冲

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

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

刷新输出缓冲区

除了 endl,它完成换行并刷新缓冲区的工作。IO库中还有两个类似的操纵符: flushendsflush 刷新缓冲区,但不输出任何额外的字符; ends向缓冲区插入一个空字符,然后刷新缓冲区:

cout << "hi! " << endl;			//输出hi和一个换行,然后刷新缓冲区
cout << "hi! " << flush;		// 输出hi,然后刷新缓冲区,不附加任何额外字符
cout << "hi! " << ends;			//输出hi和一个空字符,然后刷新缓冲区

unitbuf操纵符

如果想在每次输出操作后都刷新缓冲区,我们可以使用 unitbuf 操纵符。

  • 它告诉流在接下来的每次写操作之后都进行一次flush 操作。
  • nounitbuf操纵符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制:
cout << unitbuf;              //所有输出操作后都会立即刷新缓冲区
//任何输出都立即刷新,无缓冲
cout << nounitbuf;            //回到正常的缓冲方式

警告:如果程序崩溃,输出缓冲区不会被刷新
如果程序异常终止,输出缓冲区是不会被刷新的。当一个程序崩溃后,它所输出的数据很可能停留在输出缓冲区中等待打印。
当调试一个已经崩溃的程序时,需要确认那些你认为已经输出的数据确实已经刷新了。否则,可能将大量时间浪费在追踪代码为什么没有执行上,而实际上代码已经执行了,只是程序崩溃后缓冲区没有被刷新,输出数据被挂起没有打印而已。

关联输入和输出流
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作 都会先刷新关联的输出流 。标准库将coutcin关联在一起,因此下面语句

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而不是cout
cin.tie(old_tie);   //重建cin和cout间的正常关联

8.2、文件输入输出

头文件 fstream 定义了三个类型来支持文件 IO:ifstream从一个给定文件读取数据,ofstream向一个给定文件写入数据,以及fstream可以读写给定文件。

  • 我们可以用IO运算符(<<和>>)来读写文件,可以用 getline从一个ifstream 读取数据

在这里插入图片描述
当我们想要读写一个文件时,可以定义一个文件流对象,并将对象与文件关联起来。

  • 每个文件流类都定义了一个名为open的成员函数,它完成一些系统相关的操作,来定位给定的文件,并视情况打开为读或写模式。
  • 创建文件流对象时,我们可以提供文件名(可选的)。如果提供了一个文件名,则open会自动被调用:
  • 文件名既可以是库类型string对象,也可以是C风格字符数组
ifstream in(ifile);       //构造一个ifstream并打开给定文件
ofstream out;             //输出文件流未关联到任何文件

用fstream代替iostream&

在要求使用基类型对象的地方,我们可以用继承类型的对象来替代。这意味着,接受一个 iostream类型引用(或指针)参数的函数,可以用一个对应的fstream(或sstream)类型来调用。

  • 虽然readprint 的调用,定义时指定的形参分别是 istream&ostream&,但我们可以向它们传递fstream对象。
ifstream input(argv[1]);				//打开销售记录文件
ofstream output(argv[2]);				//打开输出文件
Sales_data total;						//保存销售总额的变量
if(read(input, total)){					//读取第一条销售记录
	Sales_data trans;					//保存下一条销售记录的变量
	while(read(input, trans)) {							//读取剩余记录
		if (total.isbn() == trans.isbn())				//检查isbn
			total.combine(trans);						//更新销售总额
		else {
			print(output, total) << endl;				//打印结果
			total = trans; 								//处理下一本书
		}
	}
	print(output, total) << endl;		//打印最后一本书的销售额
}else									//文件中无输入数据
	cerr << "No data? !" << endl;

成员函数open和close

如果我们定义了一个空文件流对象,可以随后调用open来将它与文件关联起来:

ifstream in (ifile); 				//构筑一个ifstream并打开给定文件
ofstream out;						//输出文件流未与任何文件相关联
out.open(ifile + ".copy");			//打开指定文件

if (out)		//检查open是否成功
				//open成功,我们可以使用文件了

//为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件。
in.close();						//关闭文件
in.open(ifile + "2");			//打开另一个文件

如果open成功,则open会设置流的状态,使得good()true,如果调用open失败,failbit会被置位。

自动构造和析构

考虑这样一个程序,它的main函数接受一个要处理的文件列表。这种程序可能会有如下的循环:

//对每个传递给程序的文件执行循环操作
for (auto p = argv + l; p != argv + argc; ++p){
	ifstream input(*p);			//创建输出流并打开文件
	if (input) {				//如果文件打开成功,“处理”此文件
		process (input);
	}else
		cerr << "couldn't open : " + string(*p);
}//每个循环步input都会离开作用域,因此会被销毁

当一个fstream对象离开其作用域时,与之关联的文件会自动关闭。在下一步循环中,input会再次被创建。

当一个fstream对象被销毁时,close会自动被调用。

文件模式

每个流都有一个关联的文件模式(file mode),用来指出如何使用文件。

每个文件流类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式。
ifstream关联的文件默认以in模式打开;
ofstream关联的文件默认以out模式打开;
fstream 关联的文件默认以inout模式打开。

在这里插入图片描述

  • 只可以对ofstreamfstream对象设定out模式。
  • 只可以对ifstreamfstream对象设定in 模式。
  • 只有当out也被设定时才可设定trunc模式。
  • 只要trunc没被设定,就可以设定 app 模式。在 app 模式下,即使没有显式指定out模式,文件也总是以输出方式被打开。
  • 默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断。为了保留以out模式打开的文件的内容,我们必须同时指定app模式,这样只会将数据追加写到文件末尾;或者同时指定in模式,即打开文件同时进行读写操作。
  • atebinary模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用。

以out模式打开文件会丢弃已有数据

  • 默认情况下,当我们打开一个 ofstream 时,文件的内容会被丢弃。阻止一个ofstream清空给定文件内容的方法是同时指定app模式:
//在这几条语句中,file1都被截断
ofstream out ("file1"); // 隐含以输出模式打开文件并截断文件
ofstream out2 ("filel", ofstream::out);// 隐含地截断文件
ofstream out3("file1", ofstream::out | ofstream::trunc);

//为了保留文件内容,我们必须显式指定app模式
ofstream app("file2", ofstream::app);// 隐含为输出模式
ofstream app2("file2", ofstream::out | ofstream::app);

保留被ofstream打开的文件中已有数据的唯一方法是显式指定 appin模式。

每次调用open时都会确定文件模式

  • 对于一个给定流,每当打开文件时,都可以改变其文件模式。
ofstream out; //未指定文件打开模式
out.open("scratchpad");//模式隐含设置为输出和截断
out.close (); //关闭 out,以便我们将其用于其他文件
out.open("precious", ofstream::app);//模式为输出和追加
out.close();

在每次打开文件时,都要设置文件模式,可能是显式地设置,也可能是隐式地设置。当程序未指定模式时,就使用默认值。

8.3、string流

sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,从string读取数据,就像string是一个IO流一样。

  • istringstreamstring读取数据,ostringstreamstring 写入数据,而头文件 stringstream既可从string 读数据也可向string写数据。
  • fstream类型类似,头文件sstream中定义的类型都继承自我们已经使用过的iostream头文件中定义的类型。
  • 除了继承得来的操作,sstream 中定义的类型还增加了一些成员来管理与流相关联的 string。下表列出了这些操作,可以对·stringstream·对象调用这些操作,但不能对其他IO类型调用这些操作。

在这里插入图片描述

使用istringstream

  • 当我们的某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用istringstream

考虑这样一个例子,假定有一个文件,列出了一些人和他们的电话号码。某些人只有一个号码,而另一些人则有多个——家庭电话、工作电话、移动电话等。我们的输入文件看起来可能是这样的:
在这里插入图片描述

//成员默认为公有
struct PersonInfo {
	string name ;
	vector<string> phones;
};

string line, word;					//分别保存来自输入的一行和单词
vector<PersonInfo>people;			//保存来自输入的所有记录

//逐行从输入读取数据,直至cin遇到文件尾(或其他错误)
while (getline(cin, line)) {
	PersonInfo info;				//创建一个保存此记录数据的对象
	istringstream record(line);		//将记录绑定到刚读入的行
	record >> info.name;			//读取名字
	while(record >> word)			//读取电话号码
		info.phones.push_back(word); //保持它们
	people.push_back(info);		//将此记录追加到people末尾
}

使用ostringstream

  • 当我们逐步构造输出,希望最后一起打印时,ostringstream是很有用的。

例如,对上一节的例子,我们可能想逐个验证电话号码并改变其格式。如果所有号码都是有效的,我们希望输出一个新的文件,包含改变格式后的号码。对于那些无效的号码,我们不会将它们输出到新文件中,而是打印一条包含人名和无效号码的错误信息。

由于我们不希望输出有无效电话号码的人,因此对每个人,直到验证完所有电话号码后才可以进行输出操作。但是,我们可以先将输出内容“写入”到一个内存ostringstream 中:

for (const auto &entry : people){			//对people中每一项
	ostringstream formatted, badNums; 		//每个循环步创建的对象
	for(const auto &nums : entry.phones){	//对每个数
		if(!valid(nums)){
			badNums << " " << nums;//将数的字符串形式存入badNums
		} else
			//将格式化的字符串“写入”formatted
			formatted << " " << format(nums);
	}
	if (badNums.str().empty())				//没有错误的数
		os << entry.name <<	" "				//打印名字
			<< formatted.str() << endl;		//和格式化的数
	else //否则,打印名字和错误的数
		cerr << "input error: " << entry.name
			<< " invalid number(s) " << badNums.str() << endl;
}

在此程序中,我们假定已有两个函数,validformat,分别完成电话号码验证和改变格式的功能。

程序最有趣的部分是对字符串流formattedbadNums 的使用。

我们使用标准的输出运算符(<<)向这些对象写入数据,但这些“写入”操作实际上转换为string操作,分别向formattedbadNums 中的string对象添加字符。

总结:

C++使用标准库类来处理面向流的输入和输出:

  • iostream处理 控制台 IO
  • fstream处理 命名文件 IO
  • stringstream完成 内存 string 的 IO

fstreamstringstream都是继承自类 iostream的。输入类都继承自istream,输出类都继承自 ostream
因此,可以在istream对象上执行的操作,也可在ifstreamistringstream对象上执行。继承自ostream 的输出类也有类似情况。

1、每个IO对象都维护一组条件状态,用来指出此对象上是否可以进行IO操作。
2、如果遇到了错误——例如在输入流上遇到了文件末尾,则对象的状态变为失效,所有后续输入操作都不能执行,直至错误被纠正。
3、标准库提供了一组函数,用来设置和检测这些状态。

注:如有不足,欢迎指正!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java中的注解,自定义注解

文章目录1. 注解概述2. 注解与注释3. 注解的重要性4. 常见的Annotation作用4.1 生成文档相关的注解4.2 在编译时进行格式检查(JDK内置的三个基本注解)5. 元注解6. 自定义注解6.1 定义自定义注解6.2 使用自定义注解6.3 读取和处理自定义注解框架 注解 反射 设计模式 1. 注解概…

PC安装虚拟化平台趟坑记录

合肥先进光源永磁多极铁电机控制系统的规划 Zstack EPICS Archiver在小课题组的使用经验 神仙同学的永磁四极铁样铁已经开始加工了&#xff0c;过一个月左右就要回来了&#xff0c;电机控制部分交给留国做&#xff0c;调试的也差不多了。项目买过一台工控机&#xff0c;到时候…

STM32F407ZGT6实现OLED显示屏

1、调试工具 2、OLED简介 3、硬件电路&#xff08;接线&#xff09; 本文采用7脚,倘若采用4脚&#xff0c;资料代码啥的可以在江科大B站视频下载&#xff1a; 资料下载&#xff1a;https://pan.baidu.com/s/1SqKyKr5Fsl_9gBJi8aVxTw&#xff0c; 提取码&#xff1a;8kzh&#x…

日本首相会见奥特曼,考虑引入 ChatGPT 技术

文&#xff5c;小戏卖萌屋日本4月12日电&#xff0c;日本国第101任首相&#xff0c;日本自民党总裁岸田文雄4月10日于东京会见了奥特曼先生&#xff0c;二人就 ChatGPT 引入日本的可能性问题交换了意见并进行了深入的讨论。奥特曼先生表示&#xff0c;希望为日本人创造伟大的东…

NumPy 秘籍中文第二版:三、掌握常用函数

原文&#xff1a;NumPy Cookbook - Second Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 在本章中&#xff0c;我们将介绍许多常用函数&#xff1a; sqrt()&#xff0c;log()&#xff0c;arange()&#xff0c;astype()和sum()ceil()&#xff0c;modf()&…

如何突破LinkedIn领英限制,导出非好友邮箱等社交方式

相信做外贸的朋友都有使用过Linkedin&#xff0c;如果还没有使用过的话&#xff0c;我只能说您错过一个很好的平台。只要是厉害的外贸人都特别擅长用Linkedin找客户。 那为什么说Linkedin是外贸业务员开发客户最有效的途径呢&#xff1f;主要基于以下几点&#xff1a; 第一&a…

常见的相似度计算方式

1.欧氏距离(Euclidean Distance) 欧氏距离(也称欧几里得度量)指在mmm维空间中两个点之间的真实距离&#xff0c;或者向量的自然长度(即该点到原点的距离)。 在二维和三维空间中的欧氏距离就是两点之间的实际距离。 计算公式&#xff1a; dist(A,B)∑i1n(Ai−Bi)2dist(A,B)\sqr…

DIN35电压电流转频率单位脉冲输出信号变换器集电极开路隔离变送器

主要特性 将直流电压或电流信号转换成单位脉冲信号。 精度等级&#xff1a;0.1 级、0.2 级。产品出厂前已检验校正&#xff0c;用户可以直接使用。 国际标准信号输入:0-5V/0-10V/1-5V 等电压信号,0-10mA/0-20mA/4-20mA 等电流信号。 输出标准信号&#xff1a;0-5KHz/0-…

MySQL 索引常见问题汇总,一次性梳理

hello&#xff0c;大家好&#xff0c;我是张张&#xff0c;「架构精进之路」公号作者。 提到MySQL查询分析&#xff0c;就会涉及到索引相关知识&#xff0c;要想学好MySQL&#xff0c;索引是重要且不得不啃下的一环&#xff0c;今天就把MySQL索引常见问题进行汇总&#xff0c;一…

vue.js实现带表情评论功能前后端实现 (滚动加载效果)

学习链接 vue.js实现带表情评论功能前后端实现&#xff08;仿B站评论&#xff09; 实现在vue项目中通过滚动条来滑动加载数据 IntersectionObserver与无限滚动加载 效果图 每次加载2条数据 思路 要实现滚动加载&#xff0c;就是当滚动条滚动到底部的时候&#xff0c;再去请…

零基础如何备考2023年系统集成项目管理工程师中级

系统集成项目管理工程师也是属于软考中级稍微好考一点的&#xff0c;管理类的知识多背多记多刷题。 中级集成是好考的&#xff0c;题目也不难&#xff0c;主要弄清楚47个过程的输入输出&#xff0c;还有工具的使用&#xff0c;几乎很多题都是按照这逻辑来的。 建议可以去网上…

ai写作软件免费-ai写作助手

ai写作 AI写作是一种利用人工智能技术和自然语言处理算法&#xff0c;从大量的数据中挖掘潜在信息、规律及信息流的写作方式。通过AI写作&#xff0c;可快速生成符合规范的文章、报告、文档、邮件等内容&#xff0c;为企业和个人提高效率、减少时间成本、实现更低的文字生产总成…

MySQL查询分组Group By原理分析

目录1. 使用group by的简单例子2. group by 原理分析2.1 explain 分析2.2 group by 的简单执行流程3. where 和 having的区别3.1 group by where 的执行流程3.2 group by having 的执行3.3 同时有where、group by 、having的执行顺序3.4 where having 区别总结4. 使用 group…

PR视频导出文件大

C 选择递刀工具 CtrlK 将视频分隔 pr导出的视频文件太大&#xff0c;通过这2个方法可以大大减小视频的大小&#xff0c;并且画质还能保持清晰&#xff01; 方法一&#xff1a;修改PR导出视频设置 在导出格式中选择【H.264】&#xff0c;在下面预设的位置选择【匹配源-高比特率…

永久免费内网穿透不限制速度

市面上的免费内网穿透大都有格式各样的限制&#xff0c;什么限制流量啊&#xff0c;每个月要签到打卡啊&#xff0c;还有更改域名地址等&#xff0c;只有神卓互联内网穿透是永久免费没有限制的&#xff0c;白嫖也可以。 这篇文章分享了3个方案&#xff0c;按照性能和综合指标排…

金三银四没把握住,凉了...

大家好&#xff0c;前两天跟朋友感慨&#xff0c;今年的铜三铁四、裁员、疫情导致好多人都没拿到offer!现在互联网大厂终于迎来了应届生集中求职季。 对于想跳槽的软件测试人来说&#xff0c;绝对是个找工作的好时机。这时候&#xff0c;很多高薪技术岗、管理岗的缺口和市场需…

提高stackoverflow方法速度方法

最近有些代码问题需要访问Stack Overflow - Where Developers Learn, Share, & Build Careers 但是刷新太慢&#xff0c;心都碎了 Stack Overflow - Where Developers Learn, Share, & Build CareersStack Overflow is the largest, most trusted online community …

在C上++!(上)

目录 一、传说中的C&#xff1a; 二、C发展史 三、应用领域 四、C怎么学&#xff1f; 五、命名空间namespace 1、命名空间的作用 2、如何定义命名空间 3、嵌套定义命名空间及其访问 ​编辑 4、命名空间的合并 六、简单输入输出 七、缺省参数 1、全缺省参数 2、半/部…

【从零开始学Skynet】基础篇(五):简易聊天室

在游戏中各玩家之间都可以进行聊天之类的交互&#xff0c;在这一篇中&#xff0c;我们就来实现一个简易的聊天室功能&#xff0c;这在上一篇代码的基础上很容易就能实现。1、功能需求 客户端发送一条消息&#xff0c;经由服务端转发&#xff0c;所有在线客户端都能收到&#xf…

【Jetpack】ActivityResult介绍及原理分析

​​ 前言 本文先介绍ActivityResult的基本使用&#xff0c;最后会通过源码来探讨背后的原理。 在Android中&#xff0c;我们如果想在Activity之间双向传递数据&#xff0c;需要使用startActivityForResult启动&#xff0c;然后在onActivityResult中处理返回&#xff0c;另外…