资讯专栏INFORMATION COLUMN

C++IO流详解

trilever / 2571人阅读

摘要:在使用时候必须要包含头文件并引入标准命名空间。在该头文件下,标准库三个类进行流的输入进行流的输出进行流的输入输出将结构体的内容转换成字符串字符串的内容输出到结构体当中注意实际是在其底层维护了一个类型的对象用来保存结果。

一. C语言输入输出

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

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

二.C++输入输出

C++标准库提供了一组丰富的输入/输出功能,本文重点讲解C++编程中最常见的 I/O 操作

C++的 I/O 发生在流中,流是字节序列,如果字节流是从设备(如键盘,磁盘驱动器,网络连接等)流向内存,这叫做输入操作,如果字节流是从内存流向设备,这叫做输出操作。

标准输出流 :
预定义的对象 cout 是 ostream 类的一个实例,cout 对象"连接"到标准输出设备,通常是显示屏。

标准输入流 :
预定义的对象 cin 是 istream 类的一个实例,cin 对象附属到标准输入设备,通常是键盘。

我们来研究下面这一段代码

#include#includeusing namespace std;struct Student{	string _name;	int _age;};int main(){	Student s;	cin >> s._name >> s._age;	cout << s._name << " " << s._age << endl;	scanf("%s%d", s._name.c_str(), &s._age);	printf("%s %d", s._name.c_str(), s._age);}

这段代码看上去没有什么问题,但如果我们第二次输入的字符串过长,会导致程序崩溃,原因如下 :

string内部会有一个_Buf数组,当存储的字符串大小小于15字节时,不会去堆上开辟空间存储字符串,会将字符串存储在_Buf数组中,但无论第一次输入的字符串大小是大于15字节还是小于15字节,第二次如果输入过长,都会导致程序崩溃,原因在于第二次是用scanf进行输入,使用scanf输入,不会影响string的size和capacity,所以输入过长就会越界写入

#include#includeusing namespace std;struct Student{	string _name;	int _age;};int main(){	Student s;	cin >> s._name >> s._age;	cout << s._name << " " << s._age << endl;	scanf("%s%d", s._name.c_str(), &s._age);	cout << s._name << " " << s._age << endl;}

这段代码和上一段代码相比只是最后的输出使用了cout,跟上面一样,如果输入过长也会导致越界写入,除此之外,就算输入的长度在合法的范围内,打印出来的结果也不是我们想要的,原因是cout在输出时是根据string的size来输出的,而scanf输入时并没有改变string的size,所以打印结果不是我们想要的,上面的printf如果输入的长度在合法的范围内,打印结果就是我们想要的,原因是printf打印字符串时是根据’/0’的位置来判断的.

想要解决上面的两个问题,我们提前把空间开好即可

#include#includeusing namespace std;struct Student{	string _name;	int _age;};int main(){	Student s;	cin >> s._name >> s._age;	cout << s._name << " " << s._age << endl;	s._name.resize(100); // 提前开好空间	scanf("%s%d", s._name.c_str(), &s._age);	printf("%s %d/n", s._name.c_str(), s._age);	cout << s._name << " " << s._age << endl;}

因此,建议大家在C++中尽量去用cin和cout,用cout和cin不方便的地方,再去用scanf和printf(格式控制输出时)

三.C++ IO流

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

(1).C++标准IO流

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

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

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

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

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

#includeusing namespace std;int main(){	// 输入 hello world	string a;	cin >> a;	cout << a << endl; // hello	cin >> a;	cout << a << endl; // world	getline(cin,a);	cout << a << endl;}

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

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

(6). 循环输入结构

string str;while(cin>>str){	cout<<str<<endl;}char buff[100];while(scanf("%s",buff) != EOF){	printf("%s/n",buff);}

两种方式都能达到循环输入的目的,使用ctrl + c终止输入
(1). cin>>str 等同于 istream& operator>>(cin,str),返回值为cin对象,通过operator bool()来判断是否有读取错误
(2). scanf函数当读取发生错误或读到文件末尾,会返回EOF

(2).C++文件IO流

ofstream
输出文件流,用于创建文件并向文件写入信息
ifstream
输入文件流,用于从文件读取信息
fstream
文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息

文件常见的打开方式 :
(1). in 以读的方式打开文件
(2). out 以写的方式打开文件
(3). binary 以二进制方式对文件进行操作
(4). ate 输出位置从文件的末尾开始
(5). app 以追加的方式对文件进行写入
(6). trunc 先将文件内容清空再打开文件

常用成员函数
(1). put 插入一个字符到文件
(2). write 插入一段字符到文件
(3). get 从文件提取字符
(4). read 从文件提取多个字符
(5). tellg 获取当前字符在文件当中的位置
(6). seekg 设置对文件进行操作的位置
(7). >>运算符重载 将数据形象地以“流”的形式进行输入(用于文本文件)
(8). <<运算符重载 将数据形象地以“流”的形式进行输出(用于文本文件)

以文本的形式操作文件

对文本文件进行写入操作

//以文本的形式对文件进行写入void Write(){	ofstream outfile("test.txt"); // 以写的方式打开文件	outfile.put("c"); // 向文件写入一个字符"c"	outfile.write("hello world",5); // 向文件写入长度为n的字符串	outfile << "hehe"; // 向文件写入字符串	outfile.close(); // 关闭文件}

对文件文件进行读取操作

//以文本的形式对文件进行读取void ReadTxt(){	ifstream infile("test.txt");	// seekg(),tellg()	char c, c1, c2;	infile.seekg(2);	infile >> c;	infile.seekg(3, ios::beg);	infile >> c1;	infile.seekg(-1,ios::end);	infile >> c2;	cout << c << endl;	cout << c1 << endl;	cout << c2 << endl;	// get	char c;	while (infile.get(c))	{		cout << c;	}	cout << endl;	// read	infile.seekg(0, ios::end);	int length = infile.tellg();	infile.seekg(0, ios::beg);	char* buffer = new char[length];	infile.read(buffer, length);	if (infile)	{		cout << buffer << endl;	}	delete[]buffer;	// >>	infile.seekg(0, ios::end);	int length = infile.tellg();	infile.seekg(0, ios::beg);	char* buffer = new char[length];	infile >> buffer;	cout << buffer << endl;}

对二进制文件进行写入操作

void write(){	ofstream outfile("test.bin",ios::out | ios::binary);	outfile.put("l");	outfile.write("lyp hello linux",10);	}

对二进制文件进行读取操作

void read(){	ifstream infile("test.bin", ios::in | ios::binary);	// get	char c;	while (infile.get(c))	{		cout << c;	}	cout << endl;		// read	infile.seekg(0, ios::end);	int length = infile.tellg();	infile.seekg(0, ios::beg);	char* buffer = new char[length];	infile.read(buffer, length);	if (infile)	{		cout << buffer << endl;	}	delete[]buffer;}

四.stringstream介绍

在C语言中,如果想要将一个整形变量的数据转化为字符串格式,我们一般有以下两种方式
(1). itoa

int a = 20;char buffer[10];itoa(a,buffer,10); // 将数字转换成10进制字符

(2). sprintf

struct ServerInfo{	char _ip[20];	int _port;};int main(){	ServerInfo info = {"2020.110.400.83",250};	char buffer[128];	// 序列化,将结构体中的内容转换成字符串	sprintf(buffer, "%s %d", info._ip, info._port); // 加空格,在buffer数组里就有空格	// 反序列化,将数组的内容读到结构体中	ServerInfo rinfo;	sscanf(buffer, "%s %d", rinfo._ip, &rinfo._port);}

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

在C++中,可以使用stringstream类对象来避开此问题。
在程序中如果想要使用stringstream类,必须要包含头文件。在该头文件下,标准库三个类:
(1). istringstream 进行流的输入
(2). ostringstream 进行流的输出
(3). stringstream 进行流的输入输出

#include#includeusing namespace std;struct ServerInfo{	char _ip[20];	int _port;};int main(){	ServerInfo info = {"2020.110.400.83",250};	// 将结构体的内容转换成字符串	stringstream sm;	sm << info._ip << " " << info._port;	string buffer = sm.str();	cout << buffer << endl;		// 字符串的内容输出到结构体当中	stringstream sm;	sm.str("2020.110.400.83 250");	ServerInfo rinfo;	sm >> rinfo._ip >> rinfo._port;		stringstream sm("2020.110.400.83 250");	ServerInfo rinfo;	sm >> rinfo._ip >> rinfo._port;	}

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121139.html

相关文章

  • Java IO详解

    摘要:后缀名是父类名。将字符读取流对象作为参数传递给缓冲对象的构造函数。读写失败读取关闭失败写入关闭失败字节流操作从文件系统中的某个文件中获得输入字节。和字节流需求,想要操作图片数据。 概述 IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中。 流按操作数据分为两种:字节流与字符流流按流向分为:输入流、输出流 IO流常用基类 字符流的抽...

    DC_er 评论0 收藏0
  • 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

    摘要:的选择器允许单个线程监视多个输入通道。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动通常不会。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之...

    Meils 评论0 收藏0
  • 我的阿里之路+Java面经考点

    摘要:我的是忙碌的一年,从年初备战实习春招,年三十都在死磕源码,三月份经历了阿里五次面试,四月顺利收到实习。因为我心理很清楚,我的目标是阿里。所以在收到阿里之后的那晚,我重新规划了接下来的学习计划,将我的短期目标更新成拿下阿里转正。 我的2017是忙碌的一年,从年初备战实习春招,年三十都在死磕JDK源码,三月份经历了阿里五次面试,四月顺利收到实习offer。然后五月怀着忐忑的心情开始了蚂蚁金...

    姘搁『 评论0 收藏0
  • Node.js知识点详解(一)基础部分

    摘要:基础的端到端的基准测试显示大约比快八倍。所谓单线程,就是指一次只能完成一件任务。在服务器端,异步模式甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有请求,服务器性能会急剧下降,很快就会失去响应。 模块 Node.js 提供了exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 e...

    whjin 评论0 收藏0
  • Java NIO详解

    摘要:前言本篇主要讲解中的机制和网络通讯中处理高并发的分为两块第一块讲解多线程下的机制第二块讲解如何在机制下优化资源的浪费服务器单线程下的机制就不用我介绍了,不懂得可以去查阅下资料那么多线程下,如果进行套接字的使用呢我们使用最简单的服务器来帮助大 前言 本篇主要讲解Java中的IO机制和网络通讯中处理高并发的NIO 分为两块:第一块讲解多线程下的IO机制第二块讲解如何在IO机制下优化CPU资...

    rickchen 评论0 收藏0

发表评论

0条评论

trilever

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<