C++进阶篇11---IO流

一、对C语言的输入输出的理解

C语言中我们经常用scanf()和printf()进行输入输出,形象的描述它们的作用如下

对于缓冲区的理解:

  1. 可以屏蔽掉低级I/O的实现,低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序
  2. 可以使用这部分的内容实现"行"读取的行为,对于计算机而言是没有“行”这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一”行“的数据

二、流的概念

  • "流"即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续具有方向性抽象描述。
  • C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。
  • 它的特性是:有序连续具有方向性
  • 为了实现这种流动,C++定义了I/O标准类库,库中每个类都称为流/流类,用以完成某方面的功能

(cerr用来做错误输出,clog用来做日志输出,但是需要做配置)

注意:

1. cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了只有把输入缓冲区中的数据取完后,才要求输入新的数据

2. 输入的数据类型必须与要提取的数据类型一致,否则出错。出错只是在流的状态字state中对应位置位(置1),程序继续。

3. 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格。回车符也无法读入。

4. cin和cout可以直接输入和输出内置类型数据,原因:标准库已经将所有内置类型的输入和输出全部重载了。

5. 对于自定义类型,如果要支持cin和cout的标准输入输出,需要对<<和>>进行重载

#include<iostream>
using namespace std;class Date
{friend ostream& operator<<(ostream& out, const Date& date);friend istream& operator>>(istream& in, Date& date);
public:Date(int year=0,int month=0,int day=0):_year(year),_month(month),_day(day){}~Date(){}
private:int _year;int _month;int _day;
};istream& operator>>(istream& in, Date& date)
{in >> date._year >> date._month >> date._day;return in;
}ostream& operator<<(ostream& out, const Date& date)
{out << date._year << "年" << date._month << "月" << date._day << "日" << endl;return out;
}int main()
{Date d;cin >> d;cout << d;return 0;
}

6.可以循环输入数据,输入ctrl+z表示输入结束

	while (cin >> x){//...}

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

这里就需要讲一下类型转换的几种形式:

1、内置类型之间,类型相近就能相互转换【隐式类型转化/显示类型转换/用C++中的类型转化】

2、自定义类型之间,可以通过构造函数就i行类型转换,内置类型转自定义类型也是同理

3、自定义类型转内置类型,如何转?这里C++提供了operator type()这样一个成员函数,使用如下

class A
{
public:A(int x):_x(x){}operator bool(){return _x > 10; // 可以自己实现逻辑}
private:int _x;
};int main()
{A a(10);if (a)cout << "a>10";elsecout << "a<=10";return 0;
}

库中提供了istream => bool 的类型转换函数,故istream对象能作为判断条件

 三、文件IO流

(仅仅是介绍一写简单的读写方法,具体的还得去结合文档去查找自己需要的函数)

1、二进制读写

// 我们可以将输入输出封装成一个类方便使用,下面是一个简单二进制文件读写的封装
template<class T>
class BinIO
{
public:BinIO(string filename) :_filename(filename){}void Write(const T& tmp){ofstream ofs(_filename, ofstream::out | ofstream::binary);ofs.write((char*)&tmp, sizeof(tmp));ofs.close();}void Read(T& tmp){ifstream ifs(_filename, ofstream::in | ofstream::binary);ifs.read((char*)&tmp, sizeof(tmp));ifs.close();}private:string _filename;
};

使用如下

struct A
{int _x;double _y;char _s[20];
};int main()
{A a = { 10,3.14,"zxws-yyds" };BinIO<A> bin("test.txt");bin.Write(a);A a1;bin.Read(a1);cout << a1._x << endl;cout << a1._y << endl;cout << a1._s << endl;return 0;
}

确实也能读写出来,但是如果我们将结构体A中的char[]换成string容器就会出现问题,如下

struct A
{int _x;double _y;//char _s[20];string _s;
};int main()
{A a = { 10,3.14,"zxws-yyds" };BinIO<A> bin("test.txt");bin.Write(a);A a1;bin.Read(a1);cout << a1._x << endl;cout << a1._y << endl;cout << a1._s << endl;return 0;
}

显然代码退出异常,为什么? 这里就要清楚string容器中存放的是什么(不清楚的可以去看C++入门篇7---string类,里面有string的模拟实现),是ptr,size,capacity,也就是说string中并没有存具体数据,存的是指针,所以我们写文件的时候也是写的指针,这就导致我们在读的时候,读出了相同的地址,出现了浅拷贝问题,导致析构时,空间被释放了两次

同时,如果我们的读写在两个进程中分别执行,那么就会导致野指针的问题,因为两个进程的地址空间是不一样的,在写进程中该地址确实有效,但是在读进程中该指针指向的地址确是不合法的。

这是二进制读写容易犯得问题,只能说在存容器数据的时候要小心,因为容器一般不存放具体数据

2、文本读写

template<class T>
class TextIO
{
public:TextIO(string filename) :_filename(filename){}void Write(const T& tmp){ofstream ofs(_filename);ofs << tmp;ofs.close();}void Read(T& tmp){ifstream ifs(_filename);ifs >> tmp;ifs.close();}private:string _filename;
};

class Date
{friend ostream& operator<<(ostream& out, const Date& date);friend istream& operator>>(istream& in, Date& date);
public:Date(int year=0,int month=0,int day=0):_year(year),_month(month),_day(day){}~Date(){}
private:int _year;int _month;int _day;
};istream& operator>>(istream& in, Date& date)
{in >> date._year >> date._month >> date._day;return in;
}
ostream& operator<<(ostream& out, const Date& date)
{// out << date._year << "年" << date._month << "月" << date._day << "日" << endl; //方便读out << date._year << " " << date._month << " " << date._day  << endl;return out;
}int main()
{TextIO<Date>text("test.txt");Date d(2024, 4, 7);text.Write(d);Date d1;text.Read(d1);cout << d1;return 0;
}

四、stringstream

1. 将数值类型数据格式化为字符串
int main()
{int a = 10;string str;stringstream ss;ss << a;ss >> str;cout << str << endl;ss.clear();// clear()// 注意多次转换时,必须使用clear将上次转换状态清空掉// stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit// 因此下一次转换是必须调用clear()将状态重置为goodbit才可以转换// 但是clear()不会将stringstreams底层字符串清空掉ss.str("");// s.str("");// 将stringstream底层管理string对象设置成"", // 否则多次转换时,会将结果全部累积在底层string对象中double y = 3.14;ss << y;ss >> str;cout << str << endl;return 0;
}
2. 字符串拼接
int main()
{stringstream ss;string name = "张三";int age = 20;double height = 1.74;ss << "姓名:" << name << " 年龄:" << age << " 身高:" << height;cout << ss.str() << endl;return 0;
}

3. 序列化和反序列化结构数据

struct ChatInfo
{string _name;int _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;ChatInfo rinfo;istringstream iss(str);iss >> rinfo._name >> rinfo._id >> rinfo._date >> rinfo._msg;cout << rinfo._name << " " << rinfo._id << " " << rinfo._date << " " << rinfo._msg;return 0;
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【使用flex两端对齐加margin-right】

解决办法众多&#xff1a;https://cloud.tencent.com/developer/article/1516801 <div class"job_tabs_content"><div class"job_tab_item"></div><div class"job_tab_item"></div><div class"job_tab_i…

C语言整数和小数的存储

1.整数在内存中的存储 计算机使用二进制进行存储、运算&#xff0c;整数在内存中存储使用的是二进制补码 1.1原码、反码、补码 整数的2进制表⽰⽅法有三种&#xff0c;即 原码、反码和补码 三种表⽰⽅法均有符号位和数值位两部分&#xff0c;符号位都是⽤0表⽰“正”&am…

vue快速入门(七)内联语句

注释很详细&#xff0c;直接上代码 上一篇 新增内容 button点击事件绑定内联语句写法与要求 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wid…

C++搭建深度学习的推理框架

我们的目的是:借助C++搭建一个类似于pytorch,tensorflow的深度学习框架,对标pytorch,tensorflow实现对应的功能。由于本人能力有限,下面本人将借助C++搭建一个简单的全连接神经网络,并且尝试解释里面的算子定义和计算图构建。 算子定义 回顾pytorch里面搭建的全连接神经网…

微信小程序使用icon图标

原因&#xff1a; 微信小程序使用fontawesome库使用icon图标&#xff0c;网上有很多教程&#xff0c;按照网上说法制作&#xff0c;引入到微信小程序中&#xff0c;但是验证成功&#xff0c;只能使用部分图标&#xff0c;结果不尽如人意。后面使用阿里巴巴开源iconfont来使用ic…

A Novel Distributed File System Using Blockchain Metadata——论文泛读

Wireless Personal Communications 2023 Paper 分布式元数据论文阅读笔记整理 问题 随着来自不同来源&#xff08;如在线社交媒体、物联网、移动数据、传感器数据、黑匣子数据等&#xff09;的大量数据以指数级的速度增长&#xff0c;集群计算已成为数据处理中不可避免的一部…

【移动安全】对webview漏洞的一些分析

这次分析的app如下&#xff1a; 打开发现该app发现需要登录界面&#xff1a; 拖进jadx看一下&#xff0c;先来看一下AndroidManifest.xml文件 发现有两个类是导出&#xff0c;再来分析这两个类 这个RegistrationWebView类利用webview.loadUrl进行加载网页 java public class…

【JavaWeb】Day33.MySQL概述

什么是数据库 数据库&#xff1a;英文为 DataBase&#xff0c;简称DB&#xff0c;它是存储和管理数据的仓库。 像我们日常访问的电商网站京东&#xff0c;企业内部的管理系统OA、ERP、CRM这类的系统&#xff0c;以及大家每天都会刷的头条、抖音类的app&#xff0c;那这些大家所…

VBA技术资料MF139:在PowerPoint中添加末尾幻灯片

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

【问题处理】银河麒麟操作系统实例分享,理光打印机lpr协议打印问题处理

1.问题环境 系统版本&#xff1a;Kylin-Desktop-V10-SP1-General-Release-xxx-20221120-x86_64 内核版本&#xff1a;linux 5.4.18-44kt-generic 系统版本&#xff1a;麒麟v10 sp1 处理器&#xff1a;kx6640ma 2.问题描述 问题详细描述&#xff1a;用户通过lpr协议去连接…

Azure runbook 使用用户托管标识查看资源状态

Azure runbook 使用用户托管标识查看资源状态 在托管标识里创建用户托管标识在被查看或变更资源进行授权创建自动化账号和runbook发布脚本添加计划 在托管标识里创建用户托管标识 在被查看或变更资源进行授权 这里是选取的Analysis Services 资源 创建自动化账号和runbook 发布…

【1】初识 Python

【1】初识 Python 1、编程语言(1) 语言(2) 编程语言(3) 如何利用编程语言与计算机交流(4) 常见的编程语言(5) 语法 2、Python 简介(1) 什么是 Python(2) Python 能做什么(3) Python 的由来(4) Python的特点① 语法精简② 生态好&#xff0c;开发效率高③ Python开发初体验&…