C++第五十一弹---IO流实战:高效文件读写与格式化输出

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1. C语言的输入与输出

2. 流是什么

3. C++IO流

3.1 C++标准IO流

3.2 C++文件IO流

3.2.1 以写方式打开文件

3.2.1 以读方式打开文件

4 stringstream的简单介绍


1. C语言的输入与输出


C语言中我们用到的最频繁的输入输出方式就是 scanf () 与 printf()scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)注意宽度输出和精度输出控制。C语言借助了相应的缓冲区来进行输入与输出。如下图所示:

输入输出缓冲区的理解:

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


2. 流是什么


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

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

3. C++IO流


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

3.1 C++标准IO流


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

在使用时候必须要包含文件并引入std标准命名空间。
注意:

  • 1. cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据
  • 2. 输入的数据类型必须与要提取的数据类型一致,否则出错。出错只是在流的状态字state中对应位置位(置1),程序继续。
  • 3. 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格。回车符也无法读入。
  • 4. cin和cout可以直接输入和输出内置类型数据,原因:标准库已经将所有内置类型的输入和输出全部重载了:

 代码演示

int main()
{// 标准输出流cout << "xxxx" << endl;// 错误的标准输出流cerr << "yyyy" << endl;// 用于日志记录的标准输出流clog << "zzzz" << endl;int x = 0;// 检查流的状态是否良好cout << cin.good() << endl;// 检查是否设置了 eofbitcout << cin.eof() << endl;// 检查是否设置了failbit或badbitcout << cin.fail() << endl;// 检查是否设置了badbit cout << cin.bad() << endl << endl;cin >> x;cout << x << endl;cout << cin.good() << endl;cout << cin.eof() << endl;cout << cin.fail() << endl;cout << cin.bad() << endl << endl;return 0;
}

 运行结果

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

  • 对于IO类型的算法,一般都需要循环输入:
  • 输出:严格按照题目的要求进行,多一个少一个空格都不行。
  • 连续输入时,vs系列编译器下在输入ctrl+Z时结束 
// 单个元素循环输入
while (cin >> a)
{// ...
}
// 多个元素循环输入
while (c >> a >> b >> c)
{// ...
}
// 整行接收
while (cin >> str)
{// ...
}

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

istream& operator>> (int& val);
explicit operator bool() const;

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

日期类

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){}operator bool(){// 这里是随意写的,假设输入_year为0,则结束if (_year == 0)return false;elsereturn true;}
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;
}

代码演示一

// C++ IO流,使用面向对象+运算符重载的方式
// 能更好的兼容自定义类型,流插入和流提取
int main()
{// 自动识别类型的本质--函数重载// 内置类型可以直接使用--因为库里面ostream类型已经实现了int i = 1;double j = 2.2;cout << i << endl;cout << j << endl;// 自定义类型则需要我们自己重载<< 和 >>Date d(2022, 4, 10);cout << d;while (d){cin >> d;cout << d;}return 0;
}

运行结果 

文件读写相关类

struct ServerInfo
{char _ip[32];//string _ip;int _port;Date _date;
};struct ConfigManager
{
public:ConfigManager(const char* filename = "test.txt"):_filename(filename){}void WriteBin(const ServerInfo& info){ofstream ofs(_filename, ios_base::out | ios_base::binary);// ostream& write (const char* s, streamsize n);ofs.write((const char*)&info, sizeof(ServerInfo));}void ReadBin(ServerInfo& info){ifstream ifs(_filename, ios_base::in | ios_base::binary);// istream& read (char* s, streamsize n);ifs.read((char*)&info, sizeof(info));}void WriteText(const ServerInfo& info){ofstream ofs(_filename);// 支持流插入写文件ofs << info._ip << " " << info._port << " " << info._date;}void ReadText(ServerInfo& info){ifstream ifs(_filename);// 支持流提取读文件ifs >> info._ip >> info._port >> info._date;}
private:string _filename; // 配置文件
};

二进制方式写文件

 // 二进制方式读写文件(当成员是string时会有错误,因为string读取的是地址,// 而不是实际的字符串内容)
int main()
{ServerInfo winfo = { "192.0.0.111", 80, { 2022, 4, 10 } };// 写文件ConfigManager cf_bin("test.bin");cf_bin.WriteBin(winfo);return 0;
}

 运行结果 

 二进制方式读文件

int main()
{ConfigManager cf_bin("test.bin");ServerInfo rbinfo;// 读文件cf_bin.ReadBin(rbinfo);cout << rbinfo._ip << " " << rbinfo._port << " " << rbinfo._date << endl;return 0;
}

文本方式写文件 

int main()
{ServerInfo winfo = { "192.0.0.1xxxxxxxxxxxx", 80, { 2022, 4, 10 } };// 文本写ConfigManager cf_text("test.text");cf_text.WriteText(winfo);return 0;
}

运行结果  

文本方式读文件 

int main()
{ConfigManager cf_text("test.text");ServerInfo rtinfo;// 文本读cf_text.ReadText(rtinfo);cout << rtinfo._ip << " " << rtinfo._port << " " << rtinfo._date << endl;return 0;
}

运行结果 

3.2 C++文件IO流


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

  • ifstream ifile(只输入用)
  • ofstream ofile(只输出用)
  • fstream iofile(既输入又输出用)

2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
4. 关闭文件

3.2.1 以写方式打开文件

#include<fstream>// 写文件 ofstream
int main()
{// in:读 out:写 app:追加 binary:二进制// ofstream ofs("test.txt", ofstream::out);ofstream ofs("test.txt", ofstream::out | ofstream::app);// 写进文件中ofs << "yyyyyyyyy" << endl;return 0;
}

3.2.1 以读方式打开文件

代码演示

#include<fstream>// 读文件 ifstream
int main()
{// 二进制方式打开文件ifstream ifs("D:\\111期C++\\linux\\Linux板书\\8-12-gdb详细使用.png", ifstream::in | ifstream::binary);// 获取一个字符char ch = ifs.get();int n = 0;while (ifs.good()){++n;// cout << ch;ch = ifs.get();}cout << n << endl;cout << cin.good() << endl;cout << cin.eof() << endl;cout << cin.fail() << endl;cout << cin.bad() << endl << endl;return 0;
}

运行结果  

4 stringstream的简单介绍


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

  • 1. 使用itoa()函数
  • 2. 使用sprintf()函数

但是两个函数在转化时,都得需要先给出保存结果的空间,那空间要给多大呢,就不太好界定,而且转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃。 

代码演示

int main()
{int n = 123456789;char s1[32];_itoa(n, s1, 10);char s2[32];sprintf(s2, "%d", n);return 0;
}

运行结果  

在C++中,可以使用stringstream类对象来避开此问题。
在程序中如果想要使用stringstream,必须要包含头文件 #include<sstream>。在该头文件下,标准库三个类:istringstream、ostringstream 和 stringstream,分别用来进行流的输入、输出和输入输出操作,本文主要介绍stringstream。
stringstream主要可以用来:
1. 将数值类型数据格式化为字符串 

#include<sstream>int main()
{int a = 12345678;string sa;// 将一个整形变量转化为字符串,存储到string类对象中stringstream s;s << a;s >> sa;cout << sa << endl;// clear()// 注意多次转换时,必须使用clear将上次转换状态清空掉// stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit// 因此下一次转换是必须调用clear()将状态重置为goodbit才可以转换// 但是clear()不会将stringstreams底层字符串清空掉// s.str("");// 将stringstream底层管理string对象设置成"",// 否则多次转换时,会将结果全部累积在底层string对象中s.str("");s.clear();// 清空s, 不清空会转化失败double d = 12.34;// 将一个浮点数变量转化为字符串,存储到string类对象中s << d;s >> sa;cout << sa << endl;string sValue;sValue = s.str();// str()方法:返回stringsteam中管理的string类型cout << sValue << endl;return 0;
}

运行结果  

2. 字符串拼接 

int main()
{stringstream sstream;// 将多个字符串放入 sstream 中sstream << "first" << " " << "string,";sstream << " second string";cout << "strResult is: " << sstream.str() << endl;// 清空 sstreamsstream.str("");sstream << "third string";cout << "After clear, strResult is: " << sstream.str() << endl;return 0;
}

运行结果  

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

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;
}struct ChatInfo
{string _name; // 名字int _id;      // idDate _date;   // 时间string _msg;  // 聊天信息
};// 对标C语言sscanf sprintf
// ostringstream -> 结构信息序列化为字符串
// istringstream -> 字符串序列化结构信息
// stringstream  -> 两者均可int main()
{// 结构信息序列化为字符串ChatInfo winfo = { "张三", 135246, { 2022, 4, 10 }, "晚上一起看电影吧" };ostringstream oos;cout << winfo._name << " " << winfo._id << " " << winfo._date << " " << winfo._msg;string str = oos.str();// 通过网络发送// send(str.c_str(), str.size());cout << str << endl;// receive// 字符串解析成结构化信息ChatInfo rInfo;istringstream iss;iss.str(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;
}

运行结果  

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

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

相关文章

flask框架

Flask 1 flask简介 我们之所以在浏览器中输入localhost:8080然后就可以把webapps下面的项目文件以浏览器的方式打开&#xff0c;功臣在与tomcat。python语言写的项目&#xff0c;转换为web&#xff0c;Flask框架 轻量级web应用框架。 环境准备&#xff1a; pip install fl…

【C语言】内存函数详细讲解

文章目录 前言strerror的声明和使用字符串分类函数字符转换函数内存拷贝函数&#xff08;memcpy)memcpy的声明和使用memcpy函数的模拟实现 内存拷贝函数&#xff08;memmove&#xff09;memmove的声明和使用memmove模拟实现 内存比较函数&#xff08;memcmp&#xff09;memcmp的…

UDP_SOCKET编程实现

文章目录 socket编程接口认识struct sockaddr类 编写一个server服务Client代码查看启动结果代码修正1.获取内核分配给客户端的信息2.修正不匹配ip不能访问的问题 不同机器之间的通信利用xftp将udp_client传给wsl的ubuntu机器进行演示现在模拟在windows下的udp_client代码: 对方…

时序预测 | MATLAB实现BKA-XGBoost(黑翅鸢优化算法优化极限梯度提升树)时间序列预测

时序预测 | MATLAB实现BKA-XGBoost(黑翅鸢优化算法优化极限梯度提升树)时间序列预测 目录 时序预测 | MATLAB实现BKA-XGBoost(黑翅鸢优化算法优化极限梯度提升树)时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实现BKA-XGBoost时间序列预测&a…

WPF UpdateSourceTrigger属性

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;UpdateSourceTrigger 是一个属性&#xff0c;通常用于数据绑定中&#xff0c;它控制着何时将绑定目标&#xff08;即UI元素&#xff09;的值更新到绑定源&#xff08;即数据对象&#xff09;。当UI…

Go语言现代web开发08 if和switch分支语句

if语句 If is the most common conditional statement in programming languages. If the result of the condition caculation is positive(true), the code inside if statement will be executed. In the next example, value a will be incremented if it is less than 10…

《黑神话:悟空》:中国游戏界的新篇章

引言&#xff1a; 在数字娱乐的浪潮中&#xff0c;游戏已成为连接全球文化的重要媒介。 《黑神话&#xff1a;悟空》的问世&#xff0c;不仅是中国游戏产业的一个里程碑&#xff0c;更是文化自信的闪耀展现。 这款游戏以其独特的艺术风格和深刻的文化内涵&#xff0c;在全球范…

k8s中的认证授权

目录 一、kubernetes API 访问控制 1.1 UserAccount与ServiceAccount 1.1.1 ServiceAccount 1.1.2 ServiceAccount示例 二、认证(在k8s中建立认证用户) 2.1 创建UserAccount 2.2 RBAC&#xff08;Role Based Access Control&#xff09; 2.2.1 基于角色访问控制授权&…

RT-DETR改进策略:BackBone改进|使用StarNet改进RT-DERT,显著提升性能与效率

摘要 本文介绍了我们如何将最新的StarNet模型成功应用于实时目标检测任务中,特别是用于改进RT-DERT(一种高效的实时目标检测网络)的主干网络部分。通过详尽的实验和理论分析,我们证明了StarNet不仅能够显著增强RT-DERT的检测精度,同时保持了高效的计算性能和低延迟特性。…

C++从入门到起飞之——继承下篇(万字详解) 全方位剖析!

&#x1f308;个人主页&#xff1a;秋风起&#xff0c;再归来~&#x1f525;系列专栏&#xff1a;C从入门到起飞 &#x1f516;克心守己&#xff0c;律己则安 目录 1、派⽣类的默认成员函数 1.1 四个常⻅默认成员函数 1.2 实现⼀个不能被继承的类 ​编辑 2. 继承与友…

力扣题解2390

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述​&#xff08;中等&#xff09;&#xff1a; 从字符串中移除星号 给你一个包含若干星号 * 的字符串 s 。 在一步操作中&#xff0c;你可以&#xff1a; 选中 s 中的一个星号。 移除星号…

力扣刷题(6)

两数之和 II - 输入有序数组 两数之和 II - 输入有序数组-力扣 思路&#xff1a; 因为该数组是非递减顺序排列&#xff0c;因此可以设两个左右下标当左右下标的数相加大于target时&#xff0c;则表示右下标的数字过大&#xff0c;因此将右下标 - -当左右下标的数相加小于targ…

HashMap线程不安全|Hashtable|ConcurrentHashMap

文章目录 常见集合线程安全性HashMap为什么线程不安全&#xff1f;怎么保证HashMap线程安全 HashtableConcurrentHashMap 引入细粒度锁代码中分析总结 小结 常见集合线程安全性 ArrayList、LinkedList、TreeSet、HashSet、HashMap、TreeMap等都是线程不安全的。 HashTable是线…

C语言:数组

1. 数组的概念 数组是⼀组相同类型元素的集合&#xff1b;从这个概念中我们就可以发现2个有价值的信息&#xff1a; • 数组中存放的是1个或者多个数据&#xff0c;但是数组元素个数不能为0。 • 数组中存放的多个数据&#xff0c;类型是相同的。 数组分为⼀维数组和多维数组…

【C语言必学知识点七】你知道在动态内存管理中存在的内存泄露问题吗?遇到内存泄露时应该如何处理?今天跟你好好介绍一下如何正确使用calloc与realloc!!!

动态内存管理——动态函数&#xff08;calloc、realloc&#xff09;的使用 导读一、calloc函数1.1 函数介绍1.2 calloc的使用1.3 calloc与malloc 二、realloc函数2.1 函数介绍2.2 realloc的使用2.3 realloc的空间分配2.3.1 空间分配成功——地址的改变2.3.2 空间分配失败——内…

【在Linux世界中追寻伟大的One Piece】数据链路层

目录 1 -> 数据链路层 2 -> 对比理解“数据链路层”和“网络层” 3 -> 以太网 3.1 -> 以太网的帧格式 4 -> 认识MAC地址 4.1 -> 对比理解MAC地址和IP地址 5 -> 认识MTU 5.1 -> MTU对IP协议的影响 5.2 -> MTU对UDP协议的影响 5.3 -> MT…

ElasticSearch介绍+使用

ElasticSearch 1.背景 ElasticSearch的最明显的优势在于其分布式特性&#xff0c;能够扩展到上百台服务器&#xff0c;极大地提高了服务器的容错率。在大数据时代背景下&#xff0c;ElasticSearch与传统的数据库相比较&#xff0c;能够应对大规模的并发搜索请求&#xff0c;同…

JavaScript网页设计案例分析

JavaScript网页设计案例分析 随着互联网技术的发展&#xff0c;JavaScript 已经成为现代网页设计中不可或缺的一部分。从简单的页面交互到复杂的应用程序开发&#xff0c;JavaScript 都发挥着至关重要的作用。本文将探讨几个运用 JavaScript 进行网页设计的经典案例&#xff0…

使用Python实现多个PDF文件的合并

使用Python可以很方便地实现多个PDF文件的合并。我们可以使用PyPDF2库来完成这个任务。以下是一个实现PDF合并的Python脚本&#xff1a; import os from PyPDF2 import PdfMergerdef merge_pdfs(input_dir, output_filename):# 创建一个PdfMerger对象merger PdfMerger()# 获取…

UE5源码Windows编译、运行

官方文档 Welcome To Unreal Engine 5 Early Access Learn what to expect from the UE5 Early Access program. 链接如下&#xff1a;https://docs.unrealengine.com/5.0/en-US/Welcome/#gettingue5earlyaccessfromgithub Step 0&#xff1a;找到UE5源码 直接先上链接 https…