当前位置: 首页 > news >正文

C++----IO流(参考C++ primer)

IO流

  • C++IO库
    • 标准IO流
      • IO无拷贝和赋值
      • IO条件状态及管理 (MARK有问题)
      • 管理输出缓冲区
      • IO的运算符重载
    • 文件IO流
      • ifstream读
      • ofstream写
      • 文件读写&二进制读写
    • string流
      • istringstream
      • ostringstream
      • 使用
    • 其他
      • 加速输入输出
      • IO库再探(C++ Primer-17.5)
        • 格式化输入输出
        • 非格式化输入输出
        • 流随机访问

C++IO库

在这里插入图片描述
—截图自cplusplus.com

标准IO流

IO无拷贝和赋值

头文件iostream有三种类:

  • 从流读取数据(istream, wistream)
  • 向流提取数据(ostream, wostream)
  • 读写流(iostream, wiosream)

其中开头为w的是其对应的宽字节版本(为了支持宽字节语言)


注意: IO对象无拷贝和赋值
在这里插入图片描述
截自c++ primer :
在这里插入图片描述
摘自c++ primer

  • ------由于不能拷贝IO对象,因此我们也不能将形参或返回类型设置为流类型(参见6.2.1节,第188页)。进行I0操作的函数通常以引用方式传递和返回流。读写一个I0对象会改变.其状态,因此传递和返回的引用不能是const的(正确用法见下面自定义类重载全局<<

IO条件状态及管理 (MARK有问题)

条件状态(截自c++ primer) :
在这里插入图片描述

  • 在ios_base.h源码中有这样两段:
    在这里插入图片描述
    在这里插入图片描述
  • 1L<<0为1,1L<<1为2,1L<<2为4,但是根据实际测试与ios_base.h内的不相符,测试结果为:goodbit=0(正常可用状态)badbit=4(出现系统故障的读写错误状态(无法恢复))eofbit=1(遇到文件结束符的状态), failbit=2(读入错误数据的状态(可恢复))
    可以理解为:
    在这里插入图片描述

例如:

  • cin输入与定义的i不对应类型会产生failbit状态
    在这里插入图片描述
  • 测试fail()
    在这里插入图片描述
  • 管理条件状态
    在这里插入图片描述
  • 复位产生新状态
    在这里插入图片描述

管理输出缓冲区

导致输出缓冲区刷新有很多情况 (-----摘自C++ primer):

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

主动刷新输出缓冲区方式:

  • cout<<"test"<<endl;输出test和一个换行,然后刷新缓冲区

  • cout<<"test"<<flush;输出test,然后刷新缓冲区,不附加任何额外字符

  • cout<<"test"<<ends;输出test和一个空字符,然后刷新缓冲区

manipulators在这里插入图片描述

  • cout<<unitbuf;: 所有输出操作后都会立即刷新缓冲区
    在这里插入图片描述
  • cout << nounitbuf;: 回到正常的缓冲方式

注意:如果程序崩溃,输出缓冲区不会被刷新


关联输入输出流: 在这里插入图片描述

  • 第一种形式返回一个指向绑定输出流的指针
  • 第二种形式将对象绑定到 tiestr 并返回指向在调用之前绑定的流的指针(如果有)
  • 标准库默认讲cin和cout关联在一起:当一个输入流被关联到–个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流(交互式系统通常应该关联输入流和输出流),如下两者等价
    在这里插入图片描述
  • 解除绑定并保存久绑定:ostream* old_tie = cin.tie(nullptr);
  • 关联新的输入输出流
    在这里插入图片描述
  • cin.tie(old_tie);重建cin和cout的关联

IO的运算符重载


C++标准库提供了4个全局流对象cincoutcerrclog:

  • 使用cin进行标准输入即数据通过键盘输入到程序中 (cin是isream类的对象)
  • 使用cout进行标准输出,即数据从内存流向控制台(显示器)
  • cerr用来进行标准错误的输出
  • clog进行日志的输出

(cout、cerr、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同)
iosfwd文件中:
在这里插入图片描述
iostream中:
在这里插入图片描述


注意
在这里插入图片描述
ostream支持参数为streambuf的构造(streambuf有两种:filebuf和stringbuf)
在这里插入图片描述
istream同


C++ IO流,使用面向对象+运算符重载的方式
类ostream重载 << :

  • 成员函数在这里插入图片描述
  • 非成员函数:
    在这里插入图片描述
int i = 1;
double j = 2.2;
cout << i << endl;// 自动识别类型的本质--函数重载
cout << j << endl;// 内置类型可以直接使用--因为库里面ostream类型已经实现了

调用的并不是同一个函数,分别调用的是ostream& operator<< (int val);ostream& operator<< (double val);,同时之所以可以连续输出是因为有返回值

  • 对于内置类型可以自动识别直接使用
  • 对于自定义类型,需要全局重载 (类需要加上友元:friend ostream& operator << (ostream& out, const Date& d); ),以一个成员为内置类型的自定义日期类为例
ostream& operator << (ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}

类istream重载 >> :

  • 成员函数:
    在这里插入图片描述

  • 非成员函数:
    在这里插入图片描述

  • 自定义类型Date重载>>:

istream& operator >> (istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

对于OJ题中的io输入常出现的while (cin >> str)如何结束:换行+ctrlZ
while里的cin>>str实际上是operator>>(cin, str)

  • 在C++98中istream类里重载了void*
    在这里插入图片描述
  • C++11中istream类里重载了bool
    在这里插入图片描述

支持ios及其继承类型的对象支持显示类型转换成bool,也就是以为这些类型的对象可以去做条件逻辑判断
以C++11来说也就是:while ((cin >> str).operator bool()),cin在完成cin>>i之后,将自己转换为了一个bool值,当接收到错误状态,bad、eof、fail在对应错误位置时,cin会在错误状态,就返回false,即循环中止,结束

  • 注意:这里是显式的类型转换,istream类定义了一个explicit operator bool() {}的成员函数。也就是显式的类型转换运算符函数,需要将此类的对象转换为另一种类型时,需要显式调用,如 (bool)cin 或 static_cast(cin),而若这个成员函数不是explicit的,则可以进行隐式转换,则形如cout<<cin+1;这样的语句就合法了,但是while的条件部分并没有显式转换为什么也合法呢?其实是因为一个例外:即如果表达式被用作条件,则编译器会将显式的类型转换自动应用于它
    参考:C++ 类型转换运算符;条件判断部分使用流对象的实质:while(cin>>i) { }

对于自定义类型Date如果也要这样使用可以重载 operator bool()
逻辑可以自订(这里是成员_year=0时终止)

  • 在类Date内部加上成员函数operator bool()
operator bool()
{
	if (_year == 0)
		return false;
	else
		return true;
}

想要这样调用while ((cin>>d).operator bool()),需要改库文件里istream里的bool重载,没有意义

文件IO流

C++推荐使用流来读取配置文件
注意:

  • 检查if(out):如果open失败,failbit会被置位。如果open失败,条件会为假,我们就不会去使用out了
  • 当一个fstream对象离开其作用域时,与之关联的文件会自动关闭
  • 默认情况下,当我们打开一个ofstream时,文件的内容会被丢弃。阻止一个ofstream清空给定文件内容的方法是同时指定app模式:

ifstream读

这里是引用
在这里插入图片描述

注意

  • 删除了拷贝构造
  • 可以创建一个无参文件再使用成员函数open手动打开
  • 使用initialization(2)构造,其中的mode有:(多个使用 ‘|’ 连接)
    在这里插入图片描述
  • 继承了istream的成员函数
    对于内置类型直接使用>>读出文件内容在这里插入图片描述

ofstream写

这里是引用
ofstream同理:

  • 继承ostream的成员函数:
    对于内置类型直接<<写入文件在这里插入图片描述

文件读写&二进制读写

使用文件IO流用文本及二进制方式模拟读写配置文件:

  • 文件信息:
struct ServerInfo
{
	char _address[32];
	//string _address;
	int _port;
};
  • 操作:
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));
	}
	void WriteText(const ServerInfo& info)
	{
		ofstream ofs(_filename);
		ofs << info._address <<endl<<info._port;
	}
	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_filename);
		ifs >> info._address >> info._port;
	}
private:
	string _filename; // 配置文件
};

测试

  • 二进制写和文本写 注意文本写入多个值加上空格换行间隔
    对于char[32]类型的_address以二进制方式写入还可以读出,但int类型的port就是未知字符
    在这里插入图片描述
  • 二进制读和文本读
    二进制虽然本地打开是乱码,但可以正常读取
    在这里插入图片描述
    文本正常读取在这里插入图片描述

注意:二进制读写如果将_address改为string类型会出现野指针问题

  • 写入的时候是写入的string指针(string对象实际上是一个指针,内存中是什么样子,就按字节,直接写到文件中读进来也一样,按字节原模原样读进来),测试sizeof() string对象占40个字节,但string的字符串所占的空间是从堆中动态分配 的,与sizeof()无关
  • 如果分两个进程来读取,上一次写入的指针,第二个进程来读取这个指针就不一定会指向这个string了
    在这里插入图片描述
  • 即使是写了之后马上读取也会出错(析构两次)
    在这里插入图片描述
    在这里插入图片描述
    两个对象(winfo,rbinfo)的string _address地址是一样的,会析构两次,况且,写string并没有把真正的数据写进去
  • 至于文本插入没有问题的原因:string类重载了流插入(<<)和流提取(>>),遍历string所有元素返回std::ostream& out,也就是字符串本身而不是指针

对于自定义类型Date:

  • 前面已经重载了cin,cout,这里ifstream/ofstream切片继承父类istream/ostream, 直接用即可
    在这里插入图片描述

string流

istringstream

构造:这里是引用
成员函数:
在这里插入图片描述

  • str():获取/设置内容(字符串)构造空对象再用str()设置

ostringstream

同理:

在这里插入图片描述

使用

有一个学生信息struct

struct Info
{
	string _name;
	int _id;
	Date _date;
	string _msg;
};

序列化反序列化:

// 结构信息序列化为字符串
Info winfo = { "test", 10, { 2022, 9, 4 }, "message" };
ostringstream oss;
oss << winfo._name << " " << winfo._id << " " << winfo._date << " " << winfo._msg;
string str = oss.str();
cout << str << endl;
// 获取
Info rInfo;
istringstream iss(str);
iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg;
cout << "name:" << rInfo._name << " (" << rInfo._id << ") ";
cout << rInfo._date << endl;
cout << rInfo._name << ":>" << rInfo._msg << endl;

其他

加速输入输出

  • 标准库默认关联cin和cout底层自动会调用很多flush(同步刷新缓冲区)
  • C++为了兼容C,sync_with_stdio(输入同步开关)。如果流是同步的,则程序可以将 iostream 操作与 stdio 操作混合,并保证它们的可观察效果遵循与线程中使用的相同顺序
    在这里插入图片描述
  • 关闭提升速度:std::cin.tie(nullptr) ,std::ios::sync_with_stdio(false);

注意关闭同步后不要混用scanf ,getchar,gets,fgets,fscanf和cin

IO库再探(C++ Primer-17.5)

格式化输入输出

非格式化输入输出

流随机访问

相关文章:

  • 利用内网穿透实现无固定IP调试支付回调
  • AI/DM相关conference ddl(更新中)
  • 大脑神经网络记忆原理图,记忆力机制的神经网络
  • MySQL安装与配置
  • tf.quantization
  • CleanMyMac X的免费版电脑系统瘦身工具
  • k8s 读书笔记 - 初始化容器 Init Conatiner
  • ES8(Java API Client)查询详解
  • 内置单片机的433无线模块高速连传典型运用
  • SpringBoot学习_day7
  • 【项目】小帽课堂(一)
  • [JavaScript]_[初级]_[不使用JQuery原生Ajax提交表单文件并监听进度]
  • 笔记整体梳理
  • 【题解】同济线代习题一.6.3
  • MATLAB算法实战应用案例精讲-【智能优化算法】黑寡妇算法-BWO(附matlab代码)
  • JS 中的深拷贝与浅拷贝
  • canvas 高仿 Apple Watch 表盘
  • go append函数以及写入
  • isset在php5.6-和php7.0+的一些差异
  • JS函数式编程 数组部分风格 ES6版
  • node 版本过低
  • 从零搭建Koa2 Server
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #etcd#安装时出错
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (多级缓存)多级缓存
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (五)IO流之ByteArrayInput/OutputStream
  • (一)u-boot-nand.bin的下载
  • (转)Scala的“=”符号简介
  • (转)创业的注意事项
  • ***详解账号泄露:全球约1亿用户已泄露
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • ... 是什么 ?... 有什么用处?
  • .htaccess配置常用技巧
  • .NET Core 2.1路线图
  • .net framework4与其client profile版本的区别
  • .NET 依赖注入和配置系统
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET连接MongoDB数据库实例教程
  • .sh
  • :如何用SQL脚本保存存储过程返回的结果集
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务
  • [23] GaussianAvatars: Photorealistic Head Avatars with Rigged 3D Gaussians
  • [C#]C#学习笔记-CIL和动态程序集
  • [C++进阶篇]STL中vector的使用
  • [JavaWeb学习] idea新建web项目
  • [Java基础] Java中List.remove报错UnsupportedOperationException
  • [LeetCode][138]【学习日记】深拷贝带有随机指针的链表
  • [MySQL] 二进制文件
  • [Oh My C++ Diary]类继承和类组合(内嵌类)初始化的不同