【C++】文件IO

目录

  • 一、C语言的输入输出
  • 二、流的概念
  • 三、operator bool
  • 四、C++文件IO流
    • ifstream和ofstream
    • ostringstream和istringstream
    • stringstream

一、C语言的输入输出

C语言中我们用到的最频繁的输入输出方式就是 scanf() 和 printf()。

scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中。

printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。注意宽度输出和精度输出控制。

除此之外,C语言借助了相应的缓冲区来进行输入和输出。如下图所示:
【C++】文件IO_第1张图片
对输入输出缓冲区的理解:

1.可以屏蔽掉低级 I/O 的实现,低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序。

2.可以使用这部分的内容实现 “行” 读取的行为,对于计算机而言是没有 “行” 这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一个“行”。

二、流的概念

“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续具有方向性 的数据( 其单位可以是 bit,byte,packet )的抽象描述。

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

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

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

三、operator bool

我们之前在写OJ题的时候经常可以看到如下代码

int main()
{
	string s;
	while (cin >> s)
	{
		cout << s << endl;
	}
	return 0;
}

目前我们认为cin是一个全局的类,cin>>s 的返回值其实就是cin这个全局类运算符重载>>返回的值,我们可以看一下
【C++】文件IO_第2张图片
我们可以看到,重载的>>运算符返回值是一个istream的对象,不能进行while的判断,下面我们学习以下原理。

我们可以看到,内置类型可以通过隐式类型转化为自定义类型,自定义类型无法转化为内置类型

【C++】文件IO_第3张图片

此时我们看到没有报错,c++一个特例,使用opertor重载,可以将自定义类型转化为内置类型

【C++】文件IO_第4张图片

此时,我们可以理解上面的demo
【C++】文件IO_第5张图片
【C++】文件IO_第6张图片

四、C++文件IO流

我们再C++中使用的cin和cout都是继承父类,父类继承ios实现的,iostream使用菱形继承实现,同时具有istream和ostream的功能。

【C++】文件IO_第7张图片
上面提到的operator bool就是基类IOS实现的,子类都没有去重写

  • cout为标准输出,将数据从内存流中输入到显示器上
  • cin为标准输入,通过键盘输入数据到程序中
  • cerr用于标准错误的输出
  • clog进行日志输出

其中需要注意的一点是,空格和回车会被当作数据之间的分隔符,所以字符串中不能有空格,回车和空格也不能通过cin读入

如果需要读入带空格的完整一行,可以使用getline函数

为什么cin和cout可以输入输出所有类型? 因为库里面已经将所有类型通过操作符重载<< 和>>实现了,达到了自动类型识别的效果
【C++】文件IO_第8张图片【C++】文件IO_第9张图片

之前将数据写入文件

#define _CRT_SECURE_NO_WARNINGS 1
#include

using namespace std;

#include

//IO流

class Date
{
public:
	Date(int year,int month,int day)
		:_year(year),_month(month),_day(day)
	{}

	operator string()
	{
		string str;
		str += to_string(_year);
		str += ' ';
		str += to_string(_month);
		str += ' ';
		str += to_string(_day);

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

int main()
{
	Date d(2024, 2, 3);
	
	FILE* file = fopen("file.txt", "w");
	//fwrite(&d, sizeof(d), 1, file);
	//fclose(file);

	string str = d;
	fputs(str.c_str(), file);
	fclose(file);

	return 0;
}

【C++】文件IO_第10张图片

我们会发现将数据写入文件比较容易,如果再将数据读出来发现比较费劲,这也是我们以前学习写入文件不足的地方,下面我们学习以下C++如何对文件进行IO

ifstream和ofstream

#define _CRT_SECURE_NO_WARNINGS 1
#include

using namespace std;

#include
#include

//IO流

class Date
{
public:
	Date() {};
	Date(int year,int month,int day)
		:_year(year),_month(month),_day(day)
	{}

	operator string()
	{
		string str;
		str += to_string(_year);
		str += ' ';
		str += to_string(_month);
		str += ' ';
		str += to_string(_day);

		return str;
	}

public:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}

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

struct ServerInfo
{
	char _address[32];
	int _port;
	Date _date;
};

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 << " " << info._port << " " << info._date;
	}
	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_filename);
		ifs >> info._address >> info._port >> info._date;
	}

private:
	string _filename; // 配置文件
};




int main()
{
	ServerInfo winfo = { "192.0.0.1", 80, { 2022, 4, 10 } };

	// 二进制读写

	ConfigManager cf_bin("test.bin");
	cf_bin.WriteBin(winfo);
	ServerInfo rbinfo;
	cf_bin.ReadBin(rbinfo);
	cout << rbinfo._address << " " << rbinfo._port << " "<< rbinfo._date << endl;

	// 文本读写
	//ConfigManager cf_text("test.text");
	//cf_text.WriteText(winfo);
	//ServerInfo rtinfo;
	//cf_text.ReadText(rtinfo);
	//cout << rtinfo._address << " " << rtinfo._port << " " <
	return 0;
}

二进制读写
【C++】文件IO_第11张图片
文本读写
【C++】文件IO_第12张图片

可以看到,我们以前学的文件IO只有二进制读写比较方便,但是二进制读写可读性差,C++中使用ifstream和ofstream无论再二进制还是文本读写都非常方便。

ostringstream和istringstream

这个类可以将不同的类型转为字符串

这种操作被称为序列化和反序列化,在处理自定义类型的时候非常好用

struct Date
{
public:
	friend ostream& operator << (ostream& out, const Date& d);
	friend istream& operator >> (istream& in, Date& d);
	Date(int y=0, int m=0, int d=0)
	{
		_year = y;
		_month = m;
		_day = d;
	}
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;
}

void test4()
{
	int i = 123;
	double d = 44.55;
	ostringstream oss;//序列化
	oss << i;
	string stri = oss.str();

	oss.str("");//清空oss
	oss << d;
	string strd = oss.str();
	cout << strd<< endl;

	oss.str("");//清空oss
	Date d1(2022, 10, 11);
	oss << d1;
	string strdt = oss.str();
	cout << strdt << endl;

	istringstream iss(strdt);//反序列
	Date d2;
	iss >> d2;
	cout << d2 << endl;
}

在这里插入图片描述

stringstream

这个对象可以用于字符串拼接,也可以用来将其他类型转为str
【C++】文件IO_第13张图片

  • stringstream实现转换,实际上是维护了一个string对象实现的

  • 我们可以使用str(“”)清空里面的string对象,设置为空字符串

  • 多次数据类型转换的时候,需要用clear()来清空,才能正确转换。不过clear()不会清空底层的string对象

  • 因为使用的是string对象,所以使用的时候不需要格式化控制,可以自动推导类型

stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit
因此下一次转换是必须调用clear()将状态重置为goodbit才可以继续转换
【C++】文件IO_第14张图片
这里在第一次调用stringstream操作后,我们没有进行clear,会发现后续的double类型转换失败了

【C++】文件IO_第15张图片
执行了clear之后,转换成功

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