C++ —— IO流

文章目录

      • 1. 流的定义
      • 2. c++的IO流
        • 2.1 < iostream >
        • 2.2 < fstream >
          • 2.2.1 C语言的文件IO
          • 2.2.2 C++的文件IO
          • 2.2.2.1学习俩个类
          • 2.2.2.2 使用C++的文件IO
        • 2.3 < sstream >

前言: 我们学习语言时,第一个程序可能就是输出hollow world。但是怎么输出到屏幕上的呢?这原理是怎样的呢?本文就来详细的解析此原理。


1. 流的定义

C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。

它的特性是:有序连续、具有方向性

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


2. c++的IO流

c++实现了庞大的类库,< ios >为基类,其余的都是派生类或间接派生类。
C++ —— IO流_第1张图片

2.1 < iostream >

这是我们常包的头文件,里面有cin,cout,cerr,clog。

  • cin:进行标准输入,数据从键盘上输入到程序中
  • cout:进行标准输出,数据从内存输出到显示器上
  • cerr:进行标准错误输出
  • clog:进行日志输出

cin,cout支持多种类型的输入输出,本质因为其重载了操作符<<<<,我们可以以cout为例,查看一下:
C++ —— IO流_第2张图片
就是函数重载,使得其如此的方便,比C语言还优化在哪里呢?那就是我们可以重载<<,>>。完成自定义类型的输入输出。
比如:
我定义一个日期类,并且重载<<,>>

#include
using namespace std;
class Date
{
	friend ostream& operator<<(ostream& ofs, Date& d);
	friend istream& operator>>(istream& i, Date& d);
public:
	Date(int year = 2022, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& o, Date& d)
{
	o << d._year << "/" << d._month << "/" << d._day << endl;
	return o;
}
istream& operator>>(istream& i, Date& d)
{
	i >> d._year >> d._month >> d._day;
	return i;
}


int main()
{
	Date a;
	cin >> a;
	cout << a;

	return 0;
}

重载这个<<和>>会使得向自定义类型输入输出非常舒服,这正是cout和cin的厉害之处。
运行一下:
C++ —— IO流_第3张图片


连续输入,连续输出是如何实现的呢?
C++ —— IO流_第4张图片

利用的是引用返回,返回的依旧是那个istream或ostream对象,从而做到连续的输入输出。

在做题时,有可能会要求连续输入:
a是个字符串数组:

  • C语言: while(scanf(“%s”,a)!=EOF)
  • c++: while(cin>>a)

这样就完成了多组输入,如果想要中断C语言的输入,可以按下ctrl + z,利用信号来结束输入。我们来看下EOF是什么?
C++ —— IO流_第5张图片
C++中,封装成了一个函数:eof()
C++ —— IO流_第6张图片
也就是说,如果遇到非法输入,或是信号中断那么就会返回EOF或者eof(),所以我们分别看一下scanf()和cin的返回值。
C++ —— IO流_第7张图片
scanf返回值是int,其实scanf返回的是在屏幕上打印的变量个数,EOF在C语言中是-1,在上图可以看到,所以输入流停止输入时,会返回值-1,也就是EOF。

cin重载了<<,返回值是一个istream对象,怎么能放到while里做判断呢?我想说的是istream里继承了bool(),我们可以看一下:
C++ —— IO流_第8张图片
也就是说,while判断的是对象中的bool()。至于调到了对象的bool(),这是编译器做了优化。


注意:cin用的是空格或者换行,做的分割。所以空格是输入是无法读取的,所以要用到getline()。
C++ —— IO流_第9张图片
类似于这样调用:

getline(cin,str);
2.2 < fstream >

文件的io流,向一个文件中写入或者读取数据。
C++根据文件内容的数据格式分为二进制文件和文本文件。

2.2.1 C语言的文件IO

在将fstream前,我想聊聊C语言的文件IO,对比着学习,也能复习一下:

(1) 打开文件,关闭文件:

常用以下俩个函数接口:
fopen()的第一个参数是字符串,也就是文件名;第二个参数也是字符串,表示打开文件的操作模式。
fclose()的参数是一个文件指针,关闭文件。

fopen()的第二参数比较有意思:
C++ —— IO流_第10张图片

  • r 表示只读,这个文件必须已经存在,可以取出文件中的内容
  • w 表示只写,向文件中写入内容,文件可以不存在,不存在的话可以创建;如果文件已经存在那么会清空文件的内容,进行重写
  • a 打开文件附加写入,如果文件不存在创建新文件
  • r+ 表示可读可写,文件必须已经存在
  • w+ 创建新的文件供读取并写入,如果文件已经存在则清空
  • a+ 打开文件可读取,附加写入,如果文件不存在创建新文件

如果是二进制读写,只需要第二个参数加上b,比如:wb,rb,ab……

FILE * fopen ( const char * filename, const char * mode );
int fclose ( FILE * stream );

(2) 文件的读写操作

// 二进制读写
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
// 文本读写
int fprintf ( FILE * stream, const char * format, ... );
int fscanf ( FILE * stream, const char * format, ... );
  1. 二进制读写操作
// 定义一个结构体
struct info
{
	char a[20];
	int s;
};
// 向文件中写
void test_wbin()
{
	info a = { "hollow",10};
	FILE* fi = fopen("test.bin", "wb");

	fwrite(&a, sizeof(a), 1, fi);

	fclose(fi);
}
// 读取文件的内容
void test_rbin()
{
	info b;
	FILE* fi = fopen("test.bin", "rb");

	fread(&b, sizeof(b), 1, fi);

	fclose(fi);
	// 打印到屏幕上
	cout << b.a << " " << b.s;
}

int main()
{
	test_wbin();
	test_rbin();
	return 0;
}

这是向文件test.bin写入的内容,
C++ —— IO流_第11张图片
读取文件的内容到b中,再打印到屏幕上
C++ —— IO流_第12张图片

  1. 文本文件读写操作
void test_W()
{
	info a = { "holloweveryone",10 };
	FILE* fi = fopen("test.txt", "w");

	fprintf(fi, "%s %d", a.a, a.s);

	fclose(fi);
}

void test_R()
{
	info b;
	FILE* fi = fopen("test.txt", "r");

	fscanf(fi, "%s%d", b.a, &b.s);
	fclose(fi);
	printf("%s %d\n", b.a, b.s);
}

int main()
{
	test_W();
	test_R();
	
	return 0;
}

向test.txt写入:
C++ —— IO流_第13张图片
读取文件的内容到b中,再打印到屏幕上:
C++ —— IO流_第14张图片

注意: 我们在向文件中进行写操作时,fprintf(),格式控制时最好加上空格,或者 /n 。否则读取时,会出现问题。


2.2.2 C++的文件IO
2.2.2.1学习俩个类

C++ —— IO流_第15张图片
C++ —— IO流_第16张图片
(1) ofstream类:向文件写入

  • 构造:第一个参数是文件名,第二个参数代表打开的模式
  • 二进制写:用的接口是write()
  • 文本写:用的是 <<

C++ —— IO流_第17张图片
write的参数,第一个是要往文件写入的指针,需要强转成 const char*,第二个参数是写入的大小


在这里插入图片描述
因为重载了<<,所以直接往里面写就行了。

(2) ifstream类:读取文件

  • 构造:第一个参数是文件名,第二个参数代表打开的模式
  • 二进制写:用的接口是read
  • 文本写:用的是 >>

我简单的讲讲构造函数的第二个参数,打开的模式,每个模式都可以用 按位或 | 的方式组合使用。

为什么模式可以按位或呢?
每个模式都是整型且为2的倍数,也就是2,4,8,16……
所以二进制 表示就是010,100,1000。也就是说每个二进制位表示一种模式,如果按位或的话,对应的二进制位就会变成1,表明支持此模式。


在这里插入图片描述
read()的第一个参数,表示将文件的内容读取到某处的指针,需要强转成char*,第二个参数是读取的大小。


在这里插入图片描述
因为重载>>,所以直接读取就行了。


2.2.2.2 使用C++的文件IO
#include
#include
#include
using namespace std;
struct info
{
	char a[20];
	int s;
};
class config
{
public:
	config(const char* filename)
		:_filename(filename)
	{
	}
	// 二进制写
	void write_bin(const info& w)
	{
		ofstream ofs(_filename.c_str(), ios_base::out | ios_base::binary);
		ofs.write((const char*)&w, sizeof(info));
	}
	// 二进制读
	void read_bin(info& r)
	{
		ifstream ifs(_filename.c_str(), ios_base::in | ios_base::binary);
		ifs.read((char*)&r, sizeof(info));
	}
    // 文本写
	void write(const info& w)
	{
		ofstream ofs(_filename.c_str());
		ofs << w.a << " " << w.s;
	}
   // 文本读
	void read(info& r)
	{
		ifstream ifs(_filename.c_str());
		ifs >> r.a >> r.s;
	}
	

private:
	string _filename;
};

int main()
{   // 二进制写
	info w = { "hollow" ,10 };
	config wi ("test_bin.bin");
	wi.write_bin(w);
	// 二进制读
	info r;
	wi.read_bin(r);
	cout << r.a << " " << r.s;
	// 文本写
	info ww = { "everyone",100 };
	config wwi("test_ww.txt");
	wwi.write(ww);
	// 文本读
	info rr;
	wwi.read(rr);
	cout << rr.a << rr.s;
}

以上我创建了一个类config,私有成员是文件名。里面封装了几个函数,用于文本读写。


2.3 < sstream >

在这里插入图片描述
sstream 用于将一段数据转化为字符串,这个用的很多;我们将数据变成字符串叫做序列化,将字符串再还原成数据,叫做反序列化。

我举个最简单的例子:

#include
#include
#include
#include

using namespace std;

struct PersonInfo
{
	string _name;
	int _age;
};

int main()
{
	// 序列化
	PersonInfo info = {"张三", 18};
	ostringstream oss;
	oss << info._name <<" "<< info._age ;
	string str = oss.str();
	cout << str;


	// 反序列化
	istringstream iss(str);
	string name;
	int age;
	iss >> name >> age;
	cout << name << age;
	return 0;
}

上面的:
序列化就是将结构体info的数据,流入oss中,再使用接口str(),就转成了字符串。
反序列化就是将上面的字符串进行分割,将数据取出。


结尾语: 以上 就是 c++的IO流。

你可能感兴趣的:(c++,c++,开发语言,算法)