C++ IO流全解析:标准库中的数据处理与文件读写艺术

news2024/9/23 13:18:03

🌈 个人主页:Zfox_
🔥 系列专栏:C++从入门到精通

目录

  • 一: 🔥 C语言的输入与输出
  • 二: 🔥 流是什么
  • 三: 🔥 C++IO流
    • 🚀 3.1 C++标准IO流
    • 🚀 istream类型对象转换为逻辑条件判断值
    • 🚀 3.2 C++IO的性能
    • 🚀 3.3 C++文件IO流
  • 四: 🔥 stringstream的简单介绍
  • 🚀 共勉

一: 🔥 C语言的输入与输出

🥝 C语言中我们用到的最频繁的输入输出方式就是 scanf ()printf()

  • scanf() : 从标准输入设备(键盘)读取数据,并将值存放在变量中。
  • printf() : 将指定的文字/字符串输出到标准输出设备(屏幕)。

🎯 注意宽度输出和精度输出控制。C语言借助了相应的缓冲区来进行输入与输出。如下图所示:

💢 对输入输出缓冲区的理解:

🎯 1.可以屏蔽掉低级 I/O 的实现,低级 I/O 的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序。

🎯 2.可以使用这部分的内容实现“行”读取的行为,对于计算机而言是没有“行”这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一个“行”。

二: 🔥 流是什么

  • “流” 即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据 ( 其单位可以是bit,byte,packet ) 的抽象描述。
  • C++流 是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。
  • 它的特性是:有序连续、具有方向性 。

为了实现这种流动,C++定义了 I/O 标准类库,这些每个类都称为流/流类,用以完成某方面的功能。

三: 🔥 C++IO流

🍊 C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类。

🚀 3.1 C++标准IO流

🍐 ios_base是基类,ios 继承了 ios_base。通过 ios又分别设置了两个子类istreamostream 。这两个类分别都有一个实例对象 cincout!此外ostream还要标准错误cerr和日志输出clog。除了标准IO外,istreamostream 还有子类,文件流和string流,来提供特殊的使用!

C++标准库提供了4个全局流对象cin、cout、cerr、clog,使用cout进行标准输出,即数据从内存流向控制台(显示器)。使用cin进行标准输入即数据通过键盘输入到程序中,同时C++标准库还提供了cerr用来进行标准错误的输出,以及clog进行日志的输出,从上图可以看出:cout、cerr、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同!

再来看operator bool , 这个保证了流输入输出可以转换为bool进行一个判断。在输出输入出错时可以进行一个判断。而operator bool 会去检查四个标志值,按照对应映射返回true或false:

  1. goodbit : 表示一切正常!
  2. eofbit : 读取到结束,会设置这个比特位!
  3. failbit发生一些基本的逻辑错误,会设置这个比特位!
  4. badbit : 发生不可修复的错误,会设置这个比特位!一般不会遇到种错误!
    在这里插入图片描述
    我们来看一下这四个标志:
string str;


while (cin >> str)
{
	cout << str << endl;
}

// 原型如下  重载了 >> 和 bool  就会返回 true 或 false   通过检查failbit这个标志

while (operator>>(cin, str).operator bool())
{
	cout << cin.good() << endl;     // 1
	cout << cin.eof() << endl;      // 0  
	cout << cin.bad() << endl;      // 0
	cout << cin.fail() << endl;     // 0  

	cout << str << endl;
}

🚀 istream类型对象转换为逻辑条件判断值

🍐 实际上我们看到使用 while(cin>>i) 去流中提取对象数据时,调用的是operator>>,返回值是istream类型的对象,那么这里可以做逻辑条件值,源自于 istream 的对象又调用了 operator booloperator bool 调用时如果接收流失败,或者有结束标志,则返回false。

🚀 3.2 C++IO的性能

💢 C++为了兼容C语言,会做出一些妥协优化。C语言的缓冲区只有遇到刷新标志时才会进行刷新,而如果printf缓冲区还没有刷新,我们使用cout会出现什么情况?会先把printf缓冲区刷新出来,再打印cout输出的内容,所以cout之前会先对缓冲区进行检查!所以C++风格IO需要和C风格IO进行缓冲区同步!

并且iostream库中的缓冲区通常是动态分配的,而stdio库中的缓冲区通常是静态分配的。动态分配和释放内存比静态分配内存要慢。

在上面的输出格式中我们看到iostream库支持丰富的格式化选项,C++风格IO需要再运行时进行解析处理,但是C风格IO在编译时就已经确认好输出格式了,这减少了运行时的开销。

对于有大量IO的场景,C++的IO效率会比C风格IO慢,可以使用下面三行代码来进行优化:

ios_base::sync_with_stdio(0);
cin.tie(nullptr);
cout.tie(nullptr);
  • ios_base::sync_with_stdio(0)
    🎯 这行代码的作用是取消 C++ 的 iostream 库与 C 的 stdio 库的同步。通过将 sync_with_stdio 设置为 false,可以解除这种同步,从而提高 I/O 的性能。但是,这样做之后,就不能在同一个程序中混合使用 iostream 和 stdio 函数了,因为它们不再保持同步。

  • cin.tie(nullptr)
    🎯 在默认情况下,cin 和 cout 是绑定在一起的,这意味着每次读取 cin 或写入 cout 后,都会立即刷新 cout 的缓冲区。这确保了输入输出操作的顺序性,但可能会导致性能下降。通过将 cin 的绑定解除,可以防止在每次读取输入时自动刷新输出缓冲区,从而提高性能。

  • cout.tie(nullptr)
    🎯 类似于对 cin 的操作,这行代码将 cout 的绑定解除。默认情况下,cout 与 cin 绑定,当 cin 被读取时,cout 的缓冲区会被刷新。将 cout 的.tie()设置为 nullptr,可以防止 cout 在 cin 被读取时自动刷新,从而提高性能。

🚀 3.3 C++文件IO流

🍊 C++根据文件内容的数据格式分为二进制文件文本文件。采用文件流对象操作文件的一般步骤:

  1. 定义一个文件流对象
  • ifstream (只输入用)
  • ofstream (只输出用)
  • fstream (既输入又输出用) (继承了ifstream和ofstream)
  1. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系 。
    open:打开文件,可以设置对应的打开方式和C语言很类似。
  2. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写。
  3. 关闭文件。
打开方式功能
inInput mode (输入模式)。打开文件用于输入操作
outOutput mode (输出模式)。打开文件用于输出操作
appAppend mode (追加模式)。在每次写入时,数据将被追加到文件的末尾,而不是覆盖现有内容
binaryBinary mode (二进制模式)。以二进制方式打开文件,不进行任何字符转换。这对于非文本文件(如图像或可执行文件)是必要的。
ateAt end mode (文件末尾模式)。打开文件时,文件指针定位到文件末尾。
truncTruncate mode (截断模式)。如果文件已经存在,则在打开时将其长度截断为0,即删除文件中的所有内容

🍐 写入操作可以使用<<进行流写入,也可以通过write写入一个缓冲区字符串。读取操作可以通过>>来一个一个字符读取,也可以通过read直接读取到缓冲区中。

🌰 代码示例:

struct ServerInfo
{
	char _address[32];
	int _port;
	Date _date;
};

struct ConfigManager
{
public:
	ConfigManager(const char* filename)
		:_filename(filename)
	{}
	
	void WriteBin(const ServerInfo& info)
	{
		ofstream ofs(_filename, ios_base::out | ios_base::binary);
		ofs.write((const char*)&info, sizeof(info));
	}
	
	void ReadBin(ServerInfo& info)
	{
		ifstream ifs(_filename, ios_base::in | ios_base::binary);
		ifs.read((char*)&info, sizeof(info));
	}
	
	// C++文件流的优势就是可以对内置类型和自定义类型,都使用
	// 一样的方式,去流插入和流提取数据
	// 当然这里自定义类型Date需要重载>> 和 <<
	// istream& operator >> (istream& in, Date& d)
	// ostream& operator << (ostream& out, const Date& d)
	void WriteText(const ServerInfo& info)
	{
		ofstream ofs(_filename);
		ofs << info._address << " " << info._port << " " << info._date;
	}
	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_filename);
		ifs >> info._address >> info._port >> info._date;
	}
private:
	string _filename; // 配置文件
};

int main()
{
	ServerInfo winfo = { "192.0.0.1", 80, { 2022, 4, 10 } };
	// 二进制读写
	ConfigManager cf_bin("test.bin");
	cf_bin.WriteBin(winfo);
	ServerInfo rbinfo;
	cf_bin.ReadBin(rbinfo);
	cout << rbinfo._address << " " << rbinfo._port << " "
		<< rbinfo._date << endl;
	// 文本读写
	ConfigManager cf_text("test.text");
	cf_text.WriteText(winfo);
	ServerInfo rtinfo;
	cf_text.ReadText(rtinfo);
	cout << rtinfo._address << " " << rtinfo._port << " " <<
		rtinfo._date << endl;
	return 0;
}

四: 🔥 stringstream的简单介绍

💢 在C语言中,如果想要将一个整形变量的数据转化为字符串格式,如何去做?

  1. 使用 itoa() 函数
  2. 使用 sprintf() 函数
    但是两个函数在转化时,都得需要先给出保存结果的空间,那空间要给多大呢,就不太好界定,而且转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃。
  • 在C++中,可以使用 stringstream 类对象来避开此问题。

在程序中如果想要使用 stringstream,必须要包含头文件。在该头文件下,标准库三个类:istringstreamostringstreamstringstream,分别用来进行流的输入、输出和输入输出操作,本文主要介绍 stringstream

🍊 stringstream 主要可以用来:

  1. 将数值类型数据格式化为字符串
#include<sstream>

int main()
{
	int a = 12345678;
	string sa;
	// 将一个整形变量转化为字符串,存储到string类对象中
	stringstream s;
	s << a;
	s >> sa;
	
	// clear()
	// 注意多次转换时,必须使用clear将上次转换状态清空掉
	// stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit
	// 因此下一次转换是必须调用clear()将状态重置为goodbit才可以转换
	// 但是clear()不会将stringstreams底层字符串清空掉
	// s.str("");
	// 将stringstream底层管理string对象设置成"",
	// 否则多次转换时,会将结果全部累积在底层string对象中
	
	s.str("");
	s.clear(); // 清空s, 不清空会转化失败
	double d = 12.34;
	s << d;
	s >> sa;
	string sValue;
	sValue = s.str(); // str()方法:返回stringsteam中管理的string类型
	cout << sValue << endl;
	
	return 0;
}
  1. 字符串拼接
int main()
{
	stringstream sstream;
	
	// 将多个字符串放入 sstream 中
	sstream << "first" << " " << "string,";
	sstream << " second string";
	cout << "strResult is: " << sstream.str() << endl;
	
	// 清空 sstream
	sstream.str("");
	sstream << "third string";
	cout << "After clear, strResult is: " << sstream.str() << endl;
	return 0;
}
  1. 序列化和反序列化结构数据
struct ChatInfo
{
	string _name; // 名字
	int _id; // id
	Date _date; // 时间
	string _msg; // 聊天信息
};

int main()
{
	// 结构信息序列化为字符串
	ChatInfo winfo = { "张三", 135246, { 2022, 4, 10 }, "晚上一起看电影吧"};

	ostringstream oss;
	oss << winfo._name << " " << winfo._id << " " << winfo._date << " " << winfo._msg;
	string str = oss.str();
	cout << str << endl << endl;

	// 我们通过网络这个字符串发送给对象,实际开发中,信息相对更复杂,
	// 一般会选用Json、xml等方式进行更好的支持
	// 字符串解析成结构信息
	ChatInfo rInfo;
	istringstream iss(str);

	iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg;
	cout << "-------------------------------------------------------" << endl;
	cout << "姓名:" << rInfo._name << "(" << rInfo._id << ") ";
	cout << rInfo._date << endl;
	cout << rInfo._name << ":>" << rInfo._msg << endl;
	cout << "-------------------------------------------------------" << endl;
	return 0;
}

注意:

  1. stringstream 实际是在其底层维护了一个string类型的对象用来保存结果。
  2. 多次数据类型转化时,一定要用 clear() 来清空,才能正确转化,但 clear() 不会将 stringstream 底层的 string 对象清空。
  3. 可以使用 s. str("") 方法将底层 string 对象设置为""空字符串。
  4. 可以使用 s.str() 将让 stringstream 返回其底层的 string 对象。
  5. stringstream 使用 string 类对象代替字符数组,可以避免缓冲区溢出的危险,而且其会对参数类型进行推演,不需要格式化控制,也不会出现格式化失败的风险,因此使用更方便,更安全。

🚀 共勉

以上就是我对 C++ IO流全解析 的理解,]觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

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

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

相关文章

<<编码>> 第 10 章 逻辑与开关(Logic and Switches) 示例电路

串联电路 info::操作说明 鼠标单击开关切换开合状态 需要两个开关同时闭合才能接通电路 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/code-hlchs-examples/assets/circuit/code-hlchs-ch10-01-series-circuit.txt 并联电路 in…

windows下 MySQL8.4.2 LTS 解压版的安装使用

目录 一、下载二、解压三、创建 my.ini 文件四、安装 一、下载 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 二、解压 将下载包解压到 D 盘&#xff1a; 三、创建 my.ini 文件 D:\mysql-8.4.2-winx64 目录下创建 my.ini 文件&#xff1a; [mysql] # …

前端Vue框架实现html页面输出pdf(html2canvas,jspdf)

代码demo&#xff1a; <template><el-dialog class"storageExportDialog" :fullscreen"true" title"" :visible.sync"visible" v-if"visible" width"600px"><div id"exportContainer" …

第二期: 第一节 环境的搭建

1 找一个虚拟机。 下载工具链&#xff0c; 源码。 可以看到这个压缩包里&#xff0c;有 uboot kernel 工具链 &#xff0c;都有了。 将工具链 拷贝到 /usr/local/arm 目录下。 更改 .bashrc 2 先编译一遍&#xff0c;确定环境没问题。 压缩包里 有一个 uboot , kernle 一起…

unity3d入门教程二

unity3d入门教程二 5.1游戏对象5.2对象的显示顺序5.3对象的轴心5.4对象的父子关系6.1图片素材的准备6.2图片的切割6.3图片与渲染器7.1组件7.2transform组件 5.1游戏对象 游戏制作是在编辑器中进行的 游戏脚本是进行控制对象移动的 5.2对象的显示顺序 方法1&#xff1a;调整 Or…

【重学 MySQL】二十五、等值连接vs非等值连接、自连接vs非自连接

【重学 MySQL】二十五、等值连接vs非等值连接、自连接vs非自连接 等值连接&#xff08;Equijoin&#xff09; vs 非等值连接&#xff08;Non-equijoin&#xff09;等值连接&#xff08;Equijoin&#xff09;非等值连接&#xff08;Non-equijoin&#xff09; 自连接&#xff08;…

常见的ROM(只读存储器)及其区别(超详细)

目录 1. 掩模 ROM (Mask ROM) 2. 可编程 ROM (Programmable ROM, PROM) 3. 可擦写可编程 ROM (Erasable Programmable ROM, EPROM) 4. 电可擦写可编程 ROM (Electrically Erasable Programmable ROM, EEPROM) 5. 闪存 (Flash Memory) 6. NVRAM (Non-Volatile RAM) 各类 ROM 的主…

CTF—杂项题目

1.ctfshow-Misc入门-misc17 1 用010editer打开图片后没有直接搜到ctf&#xff1b; 2 用binwalk分析文件发现有一个bzip2的隐藏文件并将其分离&#xff1b; 3 得到一个压缩文件D86.bz2&#xff1b; 4 但使用解压命令进行解压时&#xff0c;显示文件受损&#xff1b; 5 参考别人…

gbase8s之onlog相关文章

原因&#xff1a;总是在工作中会遇到抬杠的研发说insert没入库&#xff0c;特此写一篇onlog相关的文章 语法如下&#xff1a; onlog -l -d /data2/logbackup/gbase-0003_0_Log0000002093 --获取普通表的partnum: select ltrim(lower(hex(partnum)),0x) partnum from systabl…

数模方法论-整数规划

一、基本概念 整数规划是一种特殊的线性规划&#xff0c;其中某些或所有决策变量必须是整数。线性规划的概念可以阅读下面文章&#xff1a; 数模方法论-线性规划-CSDN博客https://blog.csdn.net/qq_41489047/article/details/142134282 这种方法用于解决那些要求解变…

[数据集][目标检测]高铁受电弓检测数据集VOC+YOLO格式1245张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1245 标注数量(xml文件个数)&#xff1a;1245 标注数量(txt文件个数)&#xff1a;1245 标注…

YoloV10改进策略:上采样改进|动态上采样|轻量高效,即插即用(适用于分类、分割、检测等多种场景)

摘要 本文使用动态上采样改进YoloV10&#xff0c;动态上采样是今天最新的上采样改进方法&#xff0c;具有轻量高效的特点&#xff0c;经过验证&#xff0c;在多个场景上均有大幅度的涨点&#xff0c;而且改进方法简单&#xff0c;即插即用&#xff01; 论文&#xff1a;《DySa…

【第30章】Spring Cloud之Sentinel动态规则扩展

文章目录 前言一、规则管理及推送二、DataSource 扩展1. 引入依赖2. 规则文件3. 定义数据源信息 三、服务定义和测试1. 服务定义2. 并发测试3. 控制台查看规则 总结 前言 之前我们定义的流控和熔断规则应用每次重启之后就丢失了&#xff0c;是因为在控制定义规则这些规则仅在内…

Dotnet Publish 报错 - the process cannot access the file

Dotnet Publish 报错 - the process cannot access the file 问题描述&#xff1a; 我在使用自动化部署工具 Jenkins 时&#xff0c;遇到需要使用 dotnet publish 命令来发布.net core 项目时&#xff0c;报错了&#xff1a; The process cannot access the file because it …

若依RuoYi脚手架二次开发教程(二次开发必学技能)

文章目录 一、若依框架修改器二、新建业务模块1、在新模块中导入核心模块依赖2、父工程版本锁定3、添加新模块依赖 三、菜品管理&#xff08;主子表&#xff09;1、准备SQL并导入数据库2、配置代码生成信息3、下载代码并导入项目4、页面功能测试5、功能完善与升级改造6、页面调…

I.MX6U裸机-汇编LED灯实验

汇编基础语法参考&#xff1a;初识汇编语言-CSDN博客 本文主要参考正点原子《I.MX6U 嵌入式 Linux 驱动开发指南 》第八章 STM32 GPIO 回顾 我们一般拿到一款全新的芯片&#xff0c;第一个要做的事情的就是驱动其 GPIO&#xff0c;控制其 GPIO 输出高低电平&#xff0c;我们学习…

Python复制数组并增加一个维度

二维数组扩增到三维数组 在处理nc文件过程中&#xff0c;读取的数据如果是多个时间点的数据&#xff0c;比如绿海模式的数据&#xff0c;它的格点的维度除了经纬度还有时间和高度。在进行格点数据插值到站点数据时&#xff0c;为了保持维度一致&#xff0c;也构造一个相同维度的…

【CSS in Depth 2 精译_029】5.2 Grid 网格布局中的网格结构剖析(上)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

opencv之Canny边缘检测

文章目录 前言1.应用高斯滤波去除图像噪声2.计算梯度3.非极大值抑制4.应用双阈值确定边缘5.Canny函数及使用 前言 Canny边缘检测是一种流行的边缘检测算法&#xff0c;用于检测图像中的边缘。它通过一系列步骤将图像中的像素边缘突出显示出来&#xff0c;主要分为以下几个步骤…

ChatGLM3中的一些语法

ChatGLM3中的一些语法 flyfish 一、functools.cache functools.cache 是 Python 3.9 引入的装饰器&#xff0c;用于实现函数的结果缓存。它的全称是 functools.cache&#xff0c;能够自动缓存函数调用的结果。 import functoolsfunctools.cache def expensive_computation(…