【C++进阶之路】IO流

news2024/12/26 2:09:52

文章目录

  • 一、C语言的IO
    • 1.键盘与显示屏
    • 2. 文件与内存
    • 3.字符串与内存
  • 二、C++IO
    • 1.iostream
      • 1.1基本使用
      • 1.2operator bool
    • 2. fstream
      • 2.1二进制的文件读写
      • 2.2字符串的文件读写
    • 3. sstream
      • 3.1序列化与反序列化
      • 3.2拼接字符串
      • 3.3将数据类型转换为字符串
  • 总结

一、C语言的IO

1.键盘与显示屏

//键盘与显示屏的交互
int main()
{
	//从键盘读取格式化字符串。
	int i = 0;
	scanf("%d", &i);
	//在显示屏上打印获取到的信息。
	printf("%d", i);
	return 0;
}

2. 文件与内存

int main()
{
	FILE* fptr = fopen("test.txt", "w+");
	//w只写、w+可读可写。
	//向文件里输入格式化字符串
	fprintf(fptr, "%d hello", 123);
	
	//调整文件指针的位置为开头
	rewind(fptr);
	//从文件里面读取字符串与数字。
	char arr[15] = { 0 };
	int n = 0;
	//只能以某种形式对字符串进行解读
	fscanf(fptr, "%d%s", &n,arr);
	printf("%s %d", arr, n);
	fclose(fptr);//关闭文件并刷新缓存区。
	return 0;
}

3.字符串与内存

//字符串的输入输出,对字符串按指定方式进行解读。
int main()
{
	//从内存向字符串输出可控制的格式化字符串
	char str[100];
	sprintf(str, "hello %d", 11);//这里的11可变换为变量。
	cout << "输出的字符串为:" << str << endl;
	//从字符串向变量中读入可控制的格式化字符串
	int i = 0;
	char buf[256];
	sscanf(str, "%s %d",buf,&i);
	cout<<"输入的字符为:" << i << " " << buf << endl;
	return 0;
}
  • 总结:

  • printf 与 scanf 是显示屏与键盘的IO函数

  • sprintf 与 sscanf 是字符串与内存的IO函数

  • fprintf 与 fscanf 是内存与文件的IO函数

  • 唯一不足的就是读取只能一个一个按照顺序的进行读取,无法按照自己想法

对输入输出即 printf 与 scanf 的英文理解:

在这里插入图片描述
说明:这是基于内存的理解,方便理解printf即从内存往外输出,scanf即从外面向内存读写。

补充:程序在运行时默认打开三个流——stdin,stdout,stderror。

二、C++IO

  • c++以继承的方式,梳理了所有的流。

在这里插入图片描述
 其中<iostream>是我们经常包的头文件,现在看来竟然还是一个菱形虚拟继承,不愧是实现库的大佬,没有困难创造困难迎着困难上,不过也为我们提供一点的便利。

1.iostream

1.1基本使用

  • cin和cout是我们经常使用的。
  • cerr是面向标准错误流的,clog是应用于日志的,不过日常我们使用前两个即可。

先来看最简便的一段C++代码:

int main()
{
	int x;
	cin >> x;//从键盘对x进行输入数据
	cout << "hello world" << endl;//向显示器上打印数据
	return 0;
}

 不知各位刚学这一段代码的时候,是什么感觉,我是感觉看起来比较便捷形象易懂,只是最近再看其实就是一大堆的运算符重载形成的集合,而且当我们想要用char*打印地址时,它其实给我们呈现的是一段字符串,而不是地址, 不过用起来是真香。

对自定义类型输入和输出比如之前文章中出现的日期类:

class Date
{
	friend ostream& operator << (ostream& out, const Date& d);
	friend istream& operator >> (istream& in, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
istream& operator >> (istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
ostream& operator << (ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}
int main()
{
	Date d1;
	cin >> d1;//其原理就是调用自定义的运算符重载函数。
	cout << d1 << endl;//同理。
	return 0;
}

稍微总结一下:

  1. 使用运算符重载(流插入和流提取)进行实现,更加形象易懂。
  2. 自定义类型也可重载自己的流插入和流提取,也可使用cin和cout。
  3. 自动类型识别,无需在进行格式化控制。

说明:

运算符重载vs函数重载

  1. 有更严格的要求,要尽可能在多种状况下都能正确运行。
  2. 运算符重载则需要更多的了解系统中对表达式处理的各种默认形式,那些是你无法去改变的,只能努力适应。
  • 实用小知识

同时回想在进行刷题时,如果面临读入一整行的字符串数据,有什么方法吗?

  1. gets
char* gets(char* str);
  • 缺陷:这个函数比较危险,是因为从缓存区往str读入数据时,并不知道str所指向空间的大小,因此可能会导致使str指向的空间进行越界访问,因此是危险的。
  • 说明:C++(vs2019下)此函数已删除。
  1. fgets
char * fgets ( char * str, int num, FILE * stream );

基本使用:

int main()
{
	char buf[256];
	//从标准输入流(键盘)里读取最大不超过buf大小的一行数量,到buf数组中
	fgets(buf, sizeof(buf), stdin);
	cout << buf << endl;
	return 0;
}
  1. scanf
int main()
{
	char buf[256];
	//从标准输入流(键盘)里读取一行数量,到buf数组中
	scanf("%[^\n]", buf);//直到遇到换行才停止。
	cout << buf << endl;
	return 0;
}
  • 缺陷是跟gets一样的,不过因为比较好用编译器保留了下来,可以用宏/预处理指令进行屏蔽错误。

4.getline(C++)

  • c++里面的string里面提供了一个接口getline,可以获取一行的字符串。
#include<iostream>
#include<string>
int main()
{
	string buf;
	//从标准输入流(键盘)里读取一行数量,到buf中
	getline(cin, buf);
	cout << buf << endl;
	return 0;
}
  • 这是最便捷的也最安全的输入一行字符串的方法,因为string内部会自动进行扩容,也是C++刷题最经常用的方法。

1.2operator bool

 除此之外,我们可能对多组输入的cin,会产生疑惑 , 比如如下一段代码:

int main()
{
	int x;
	while (cin >> x)
	{
		cout << x << endl;
	}
	return 0;
}
istream& operator>> (int& val);
//返回的是cin,也就是类型为istream的对象,
//而while()里面进行判断的是bool值,两者能进行转换吗?答案是肯定能的,
//那是如何进行转换的呢?

就比如:

class A
{
public:
private:
	int _a = 0;
};
int main()
{
	A a;
	if(a)//此处会报错:表达式必须包含 bool 类型(或可转换为 bool)	
	{}
	return 0;
}
  • 那如何转换为bool呢?其实运算符重载就出现了。
class A
{
public:
	operator bool()
	{
		return _a == 0 ? false : true;
	}
private:
	int _a = 0;
};
int main()
{
	A a;
	if(a){}
	//这也间接说明,iostream是实现了operator bool的。
	return 0;
}

说明:这里的实现的运算符重载没有返回值是有点奇怪的,这里当做特殊情况对待即可,不必深究。

  • cin转bool底层原理:设置标记

在这里插入图片描述

补充:在对循环终止时,有两种方式,ctrl z(正常终止循环) / Ctrl c(退出当前程序)。

2. fstream

在进行输入/输出时可有几种模式:

在这里插入图片描述

  1. 对于fostream的对象默认打开了out,对于fistream的对象默认打开了in。
  2. 操作可通过 | 进行结合起来。
  3. truncate——对文件之前的内容进行覆盖。
  4. 对于有些对象比如fstream,可以支持输入也可以支持输出。

2.1二进制的文件读写

struct PeopleInfor
{
	PeopleInfor()
	{}
	PeopleInfor(const char* id,const char* name,int age)
		//:_id(id)
	{
		strcpy(_id, id);
		strcpy(_name, name);
		_age = age;
	}
	char _id[256];//家庭住址
	char _name[256] = {0};//名字
	int _age = 0;//年龄
};
struct ManagePeoInf
{
	//初始化
	ManagePeoInf(const char* file)
		:_FileName(file)
	{}
	//向文件以二进制写入个人信息
	void WriteBianry(const PeopleInfor& pe)
	{
		ofstream of(_FileName, ios_base::out | ios_base::binary);
								//这里的out默认已经有了,可以不写。
		of.write((char*)&pe, sizeof(pe));
	}
	void ReadBinary(PeopleInfor& pe)
	{
		ifstream in(_FileName, ios_base::in | ios_base::binary);
								//这里的in默认已经有了,可以不写。
		in.read((char*)&pe, sizeof(pe));
	}
	//向文件以二进制读取个人信息
	string _FileName;
};

void BinaryWrite()
{
	PeopleInfor pe1 = { "河南省","舜华",19 };
	ManagePeoInf m1("text.txt");
	//向文件中输出字符串x
	m1.WriteBianry(pe1);
}
void BinaryRead()
{
	PeopleInfor pe2;
	ManagePeoInf m2("text.txt");
	m2.ReadBinary(pe2);
	cout << pe2._id << " " << pe2._name << " " << pe2._age << endl;
}
int main()
{
	//向文件里以二进制的形式写入数据
	BinaryWrite();
	//从文件中以二进制的形式读取数据
	BinaryRead();
	return 0;
}

说明:这里的_id与_name并没有采用string的形式进行使用,而是char类型的数组进行的使用,其原因在于拷贝的是对象的内存,而string里面存的是指向字符串的地址,string释放时,读取在恢复时,其实是一段野指针,即使访问成功了也是幸运。

2.2字符串的文件读写

struct PeopleInfor
{
	PeopleInfor()
	{}
	PeopleInfor(const char* id, const char* name, int age)
		:_id(id)
	{
		//strcpy(_id, id);
		strcpy(_name, name);
		_age = age;
	}
	string _id;//家庭住址
	char _name[256] = { 0 };//名字
	int _age = 0;//年龄
};
struct ManagePeoInf
{
	//初始化
	ManagePeoInf(const char* file)
		:_FileName(file)
	{}
	//向文件以二进制写入个人信息
	void WriteText(const PeopleInfor& pe)
	{
		ofstream of(_FileName, ios_base::binary);
		of << pe._id << " " << pe._name << " " << pe._age;
		//这里的id是调用了string的流插入。
	}
	void ReadText(PeopleInfor& pe)
	{
		ifstream in(_FileName, ios_base::binary);
		in >> pe._id >> pe._name >> pe._age;
		//这里的_id是调用了string的流提取。
		//读取自动识别空格与换行与分割符。
	}
	//向文件以二进制读取个人信息
	string _FileName;
};
void WriteText()
{
	PeopleInfor pe1 = { "河南省","舜华",19 };
	ManagePeoInf m1("text.txt");
	m1.WriteText(pe1);
}
void ReadText()
{
	PeopleInfor p2;
	ManagePeoInf m2("text.txt");
	m2.ReadText(p2);
	cout << p2._id << " " << p2._name << " "  << p2._age << endl;
}
int main()
{
	WriteText();
	ReadText();
	return 0;
}

3. sstream

3.1序列化与反序列化

  • 此处说明:这里存在序列化与反序列化,因为在存储的形式中,要么以二进制的形式进行存储(人看不懂),要么以字符串的形式进行存储(序列化),总之在计算机用于计算的类型比如int,float,double等形式,在文件中不复存在,因此计算机要想用存储的数据进行计算,就需要将字符串转换为相应的格式再参与计算(反序列化)。
#include<iostream>
#include<string>
#include<sstream>
using namespace  std;
class Date
{
	friend ostream& operator << (ostream& out, const Date& d);
	friend istream& operator >> (istream& in, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
istream& operator >> (istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
ostream& operator << (ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}
int main()
{
	string str;
	Date d = { 2023,10,21 };
	ostringstream os;
	os << d;
	str = os.str();
	cout<<"字符串:" << str << endl;

	//再将字符串转换为日期类
	Date d1;
	istringstream is(str);
	is >> d1;
	cout<<"日期类:" << d1 << endl;
	return 0;
}

这里附上一道序列化与反序列化的题:二叉树的序列化与反序列化

3.2拼接字符串

#include<iostream>
#include<string>
#include<sstream>
using namespace  std;
int main()
{
	ostringstream os;
	os << "hello";//当然也可是一些变量,比如int
	os << " world";//同理
	cout << os.str() << endl;
	return 0;
}

说明:

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

3.3将数据类型转换为字符串

int main()
{
	int a = 12345678;
	string sa;
	// 将一个整形变量转化为字符串,存储到string类对象中
	stringstream s;
	s << a;
	s >> sa;

	s.str("");//将s维护的字符串置为空
	s.clear();
	// 说明:
	// stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit
	// 因此下一次转换是必须调用clear()将状态重置为goodbit才可以转换

	double d = 12.34;
	s << d;
	s >> sa;
	string sValue;
	sValue = s.str();
	cout << sValue << endl;
	return 0;
}

总结

 今天的分享就到此结束了,我是舜华,期待与你的下次相遇!

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

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

相关文章

企业知识库软件,快速构建企业知识分享与团队协同的软件

企业知识库是一种特殊的在线协同文档工具&#xff0c;支持包括FAQ、文档、视频、知识图谱等。从本质上讲&#xff0c;它是基于企业知识库软件从而实现内部或外部知识的沉淀、集合、更新、共享等&#xff0c;能为员工或客户提供常见问题的标准回答。 今天我就基于HelpLook &…

STP、堆叠与VRRP如何使用

✍ STP生成树用在哪里&#xff1f; ✍ STP和堆叠有什么区别&#xff1f; ✍ VRRP双网关热备份如何部署&#xff1f; --- 通过交换机组成网络是局域网&#xff0c;连接终端设备的交换机就是接入层交换机。 --- 如上组网结构单一&#xff0c;不需要网工。 容易发生单点故障&…

wireshark数据包内容查找功能详解

wireshark提供通过数据包特征值查找具体数据包的功能&#xff0c;具体查找功能如下&#xff0c; &#xff08;1&#xff09;选择查找目标区域&#xff08;也就是在哪里去匹配特征值&#xff09; 如下图&#xff0c;【分组列表】区域查找指的是在最上方的数据包列表区域查找&…

msvcr110.dll丢失的解决方法介绍,教你如何快速修复问题

在计算机系统中&#xff0c;DLL&#xff08;动态链接库&#xff09;是一种非常重要的资源。它们包含了可被多个程序共享的代码和数据。其中&#xff0c;MSVCR110.dll就是Visual Studio 2012的一个组件。然而&#xff0c;有时候我们可能会遇到“msvcr110.dll丢失”的问题&#x…

2023年传媒行业中期策略 AIGC从三个不同层次为内容产业赋能

基本面和新题材共振&#xff0c;推动传媒互联网行情上涨 AIGC 概念带动&#xff0c;传媒板块领涨 A 股 2023 年第一个交易日&#xff08;1 月 3 日&#xff09;至 6 月 2 日&#xff0c;申万传媒指数区间涨幅高达 48.38%&#xff0c;同时期沪深 300 跌幅为 0.25%&#xff0c;…

【Javascript】构造函数的参数写法

目录 写法一&#xff08;固定参数&#xff09;&#xff1a; 写法二&#xff08;对象类型的参数&#xff09; 写法一&#xff08;固定参数&#xff09;&#xff1a; 如果参数与参数的值不对应 写法一 要求位置严格对应&#xff0c;明确知道对象的属性 写法二&#xff08;对象类…

JavaScript从入门到精通系列第二十二篇:JavaScript中的toString方法和JavaScript中的垃圾回收

文章目录 一&#xff1a;toString方法 1&#xff1a;怪异的返回值[object Object] 2&#xff1a;打印对象成为一个JSON 二&#xff1a;垃圾回收&#xff08;GC&#xff09; 1&#xff1a;垃圾回收概念 2&#xff1a;JS当中的垃圾回收机制 3&#xff1a;JS中的垃圾回收算…

CyclicBarrier线程同步

关于作者&#xff1a; CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP&#xff0c;带领团队单日营收超千万。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业化变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览…

数据结构与算法—双向链表

目录 一、链表的分类 二、双向链表原理 三、实现双向链表 1、声明链表结构体 2、初始化链表 3、创建新节点 4、打印链表 5、头插&尾插 头插 尾插 6、头删&尾删 头删 尾删 7、 查找节点 8、指定节点前插入 9、删除指定节点 10、销毁链表 完整版 L…

Java三层架构、表现层-业务层-持久层

三层架构 什么是 Java 三层架构 三层架构是指&#xff1a;视图层view&#xff08;表现层&#xff09;&#xff0c;服务层service&#xff08;业务逻辑层&#xff09;&#xff0c;持久层Dao&#xff08;数据访问层&#xff09;&#xff0c; Java的三层架构是指将Java程序分为三…

会议剪影 | 思腾合力携AI服务器亮相PRCV 2023,并作主题演讲

第六届中国模式识别与计算机视觉大会&#xff08;PRCV 2023&#xff09;于2023年10月13日至15日在厦门国际会议中心酒店举办。本届会议主题为“相约鹭岛&#xff0c;启智未来”。 会议旨在汇聚国内国外模式识别和计算机视觉理论与应用研究的广大科研工作者及工业界同行&#xf…

【LeetCode刷题(数据结构与算法)】:将二叉搜索树转化为排序的双向链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 对于双向循环列表&#xff0c;你可以将左右孩子指针作为双向循环链表的前驱和后继指针&#xff0c;第一个节点的前驱是最后一个节点&#xff0c;最后一个节点的后继是第一个节点 特别地&#xff0c;我们希望可以 就地 完…

pip 时报错 no such option: --bulid-dir 的解决办法

Pycharm 安装第三方库报错及解决方案——no such option: --build-dir Pycharm 安装第三方库报错及解决方案——no such option: --build-dir 最近在学习路径规划相关内容&#xff0c;在运行GitHub上下载例程时缺少“plotly”库&#xff0c;根据网上查到的安装步骤操作&#x…

计算机组成原理 new08 电路 $\color{red}{Δ}$

文章目录 ALU基本逻辑运算复合逻辑的运算 一位全加器串行加法器串行进位的并行加法器并行进位的并行加法器(全先行进位加法器)这个明天再写。加法电路原理总结ALU和加法器有什么关系加法器原理ALU总结无符号整数/补码加减法加法器标志位的生成补码加减法发运算的溢出判断溢出电…

Python实现一个简单的http服务,Url传参输出html页面

摘要 要实现一个可以接收参数的HTTP服务器&#xff0c;您可以使用Python标准库中的http.server模块。该模块提供了一个简单的HTTP服务器&#xff0c;可以用于开发和测试Web应用程序。 下面是一个示例代码&#xff0c;它实现了一个可以接收参数的HTTP服务器&#xff1a; 代码…

C1N短网址 - 是如何做到行业领先的

今天从技术角度来聊下短网址的一些事情&#xff0c;市面上的短网址发展基本上经历了几个阶段。 短网址发展的几个阶段&#xff1a; 第一阶段&#xff1a;网址缩短&#xff0c;很纯粹的功能&#xff0c;各个大小公司都在做&#xff0c;门槛很低。典型代表&#xff1a;百度短网…

Python+playwright 实现Web UI自动化

实现Web UI自动化 技术&#xff1a;Pythonplaywright 目标&#xff1a;自动打开百度浏览器&#xff0c;并搜索“亚运会 金牌榜” 需安装&#xff1a;Playwright &#xff08;不用安装浏览器驱动&#xff09; # 使用浏览器&#xff0c;并可视化打开 browser playwright.ch…

Power BI 傻瓜入门 3. 选择Power BI的版本

本章内容包括&#xff1a; Excel与Power BI的比较选择Power BI的桌面版和服务版之间的差异了解Microsoft提供的许可选项 挑选正确版本的Power BI可能就像参观世界上最大的糖果店&#xff1a;你可以从许多细微差别的替代品中进行选择。选择可以归结为想要、需要、规模&#xf…

用HFSS仿真平面线圈的电感量

用HFSS工具仿真平面线圈的电感量 平面线圈是指在平面上绕制而成的线圈&#xff0c;如PCB上的电感线圈、无线供电使用的金属丝绕制而成的线圈等。根据线圈的不同形状可将平面线圈分为方形线圈&#xff0c;六角形线圈、八角形线圈、螺旋原型线圈等。 网络上的计算平面线圈电感量…

Tmux:终端复用器的基本使用(二)

相关阅读 Tmuxhttps://blog.csdn.net/weixin_45791458/category_12472796.html?spm1001.2014.3001.5482 上一篇文章列举了一些关于tmux中会话的基本使用方法&#xff0c;但会话并非是tmux的最强大的功能&#xff0c;tmux还能在一个会话中创建多个窗口(windows)&#xff0c;并…