29.IO流(了解)

1. C语言的输入与输出

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

image-20230818191734382

2. C++IO流

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

image-20230818192044635

#include<iostream>

using namespace std;

int main()
{
	string str;
    
    // 判断是否为真
    // 方法1(整型判断):0为假,非0为真
    // 方法2(指针判断):nullptr为假,非空为真
    // str的类型为string,cin >> str其实是重载了operator>>,即
    // istream& operator>> (istream& is, string& str);
    // cin的类型为istream
    // cin再通过隐式类型转换将istream转换为整型,或者指针,这样就可以判断其为真还是假了
	while (cin >> str)
	{
		cout << str << endl;
	}

}

image-20230822160100935

  • 原理

image-20230822161314849

演示代码1

#include<iostream>

using namespace std;

class A
{
public:
	A(int a)
		:_a1(1)
		,_a2(2)
	{}

	// 通过这个重载,将自定义类型隐式转换为内置类型(重载int)
	operator int()
	{
		return _a1 + _a2;
	}

private:
	int _a1;
	int _a2;
};

int main()
{
	// 内置类型隐式类型转换成自定义类型
	A aa1 = 1;

	// 自定义类型隐式类型转换成内置类型(这里调用了operator int();)
	int a = aa1;

	cout << a << endl;
}

这段代码演示了自定义类型 A 中的隐式类型转换,以及如何通过重载类型转换操作符 operator int() 实现将自定义类型转换为内置类型 int

代码解释

  1. 自定义类 A

    • A 包含一个构造函数和两个私有成员变量 _a1_a2
    • 没有显式定义类型转换构造函数,但是定义了一个将 A 类型转换为 int 类型的类型转换函数 operator int()
  2. 重载类型转换操作符 operator int()

    • 在类 A 中定义了将 A 类型转换为 int 类型的隐式类型转换操作符。
    • 当类 A 的对象被隐式转换为 int 类型时,将返回 _a1 + _a2 的结果。
  3. 主函数中的隐式类型转换

    • A aa1 = 1;:这里使用 int 类型的整数 1 初始化了类 A 的对象 aa1。由于定义了构造函数 A(int a),因此发生了从内置类型到自定义类型的隐式类型转换。
    • int a = aa1;:这里将类 A 的对象 aa1 隐式转换为 int 类型,通过重载的类型转换操作符 operator int(),返回 _a1 + _a2 的结果。
  4. 输出结果

    • cout << a << endl;:打印变量 a,其值为 _a1 + _a2 的结果,即 1 + 2,输出结果为 3

示例运行结果

3

通过这种方式,可以实现自定义类型到内置类型的隐式类型转换,方便地将自定义类型对象用于与内置类型相关的操作。但需要注意,过度使用隐式类型转换可能会导致代码可读性下降,应谨慎使用。

演示代码2

#include<iostream>

using namespace std;


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)
	{}

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

3.C++文件IO流

std::ofstream::open

二进制读写

#include<iostream>
#include<fstream>

using namespace std;

// 二进制读写(不可以使用string)
struct ServerInfo
{
	char _address[32];
	int _port;
};

struct ConfigManager
{
public:
    // 构造函数,私有成员变量为文件的文件名
	ConfigManager(const char* filename)
		:_filename(filename)
	{}

	// 将数据写入到文件中
	void WriteBin(const ServerInfo& info)
	{
		// ios_base是ofstream的基类,因此两个类域都是可以用的,效果是一致的
		// ofstream::out,流输出,ofstream::binary 二进制输入,输出
		// ofstream ofs(_filename, ofstream::out | ofstream::binary);
		ofstream ofs(_filename, ios_base::out | ios_base::binary);

		// 将info指向的n个字符写入到文件中(也就是输出到文件中,因此使用的是ofstream)
		ofs.write((char*)&info, sizeof(info));
	}

	// 从文件中读取数据
	void ReadBin(ServerInfo& info)
	{
		ifstream ifs(_filename, ios_base::in | ios_base::binary);

		// 从流中读取n个字符,并将其存储到info中
		ifs.read((char*)&info, sizeof(info));
	}

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

int main()
{
	ConfigManager cm("test.txt");
	ServerInfo winfo = { "192.0.0.111111111111111111", 80};
	// 写入到文件
	// cm.WriteBin(winfo);

	ServerInfo rinfo;
	// 从文件中进行读取
	cm.ReadBin(rinfo);

	cout << rinfo._address << endl;
	cout << rinfo._port << endl;

	return 0;
}
  • 错误演示(string)
#include<iostream>
#include<fstream>

using namespace std;

// 二进制读写
struct ServerInfo
{
	string _address;
	int _port;
};

struct ConfigManager
{
public:
	ConfigManager(const char* filename)
		:_filename(filename)
	{}

	// 将数据写入到文件中
	void WriteBin(const ServerInfo& info)
	{
		// ios_base是ofstream的基类,因此两个类域都是可以用的,效果是一致的
		// ofstream::out,流输出,ofstream::binary 二进制输入,输出
		//ofstream ofs(_filename, ofstream::out | ofstream::binary);
		ofstream ofs(_filename, ios_base::out | ios_base::binary);

		// 将info指向的n个字符写入到文件中(也就是输出到文件中,因此使用的是ofstream)
		ofs.write((char*)&info, sizeof(info));
	}

	// 从文件中读取数据
	void ReadBin(ServerInfo& info)
	{
		ifstream ifs(_filename, ios_base::in | ios_base::binary);

		// 从流中读取n个字符,并将其存储到info中
		ifs.read((char*)&info, sizeof(info));
	}

private:
	string _filename; // 配置文件
};
  • 情况1
// 情况1:一个进程中,同时读写,_ptr的地址相同
int main()
{
	ConfigManager cm("test.txt");
	ServerInfo winfo = { "192.0.0.111111111111111111", 80};
	// 写入到文件
	cm.WriteBin(winfo);

	ServerInfo rinfo;
	// 从文件中进行读取
	cm.ReadBin(rinfo);

	cout << rinfo._address << endl;
	cout << rinfo._port << endl;

	return 0;
}
  • 情况2
// 情况2:两个进程,先写后读,_ptr变为野指针
// 先写
int main()
{
	ConfigManager cm("test.txt");
	ServerInfo winfo = { "192.0.0.111111111111111111", 80};
	// 写入到文件
	cm.WriteBin(winfo);

	return 0;
}

// 后读
int main()
{
	ConfigManager cm("test.txt");
	ServerInfo rinfo;
	// 从文件中进行读取
	cm.ReadBin(rinfo);

	cout << rinfo._address << endl;
	cout << rinfo._port << endl;

	return 0;
}

image-20230822165442415

文本读写

#include<iostream>
#include<fstream>

using namespace std;

// 日期类
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;
		else
			return 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;
}


// 文本读写
struct ServerInfo
{
	// char和string都可以使用,对于文本读写
	// char _address[32];
	string _address;
	int _port;
	Date _date;
};

struct ConfigManager
{
public:
	ConfigManager(const char* filename)
		:_filename(filename)
	{}

	void WriteText(const ServerInfo& info)
	{
		ofstream ofs(_filename);
		// 输入换行,作为字符串的分隔符
		ofs << info._address << endl;
		ofs << info._port << endl;
		ofs << info._date << endl;
	}

	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_filename);
		ifs >> info._address;
		ifs >> info._port;

		// 因为Date已经重载了流插入和流提取,因此可以直接以流插入的方式进行写入
        // ifstream是istream的子类(派生类)
		ifs >> info._date;
	}

private:
	string _filename; // 配置文件
};
int main()
{
	ConfigManager cm("test.txt");
	ServerInfo winfo = { "192.0.0.111111111111111111", 80, {2023,4,1} };
    // 文件写入
	cm.WriteText(winfo);

    // 文件读取
	ServerInfo rinfo;
	cm.ReadText(rinfo);
	cout << rinfo._address << endl;
	cout << rinfo._port << endl;
	cout << rinfo._date << endl;

	return 0;
}

image-20230822171939920

4.stringstream(万物皆可转字符串)

#include<iostream>
#include<fstream>
#include<sstream>

using namespace std;

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;
		else
			return 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;
}
int main()
{
	int i = 1234;
	double dl = 2.22;
	Date d = { 2023, 4, 1 };

	// 输出到流oss中
	ostringstream oss;
	oss << i << " ";   // 使用空格分割字符串
	oss << dl << " ";
	oss << d << " ";
	string str = oss.str();
	cout << str << endl;

	int j;
	double dll;
	Date dd;

	// 从流iss中进行提取
	istringstream iss(str);
	iss >> j >> dll >> dd;

	return 0;
}

序列化和反序列化

#include<iostream>
#include<fstream>
#include<sstream>

using namespace std;

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;
		else
			return 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;
}

// 序列化和反序列化
struct ChatInfo
{
	string _name; // 名字
	int _id;      // id
	Date _date;   // 时间
	string _msg;  // 聊天信息
};
int main()
{
	// stringstream是ostringstream和istringstream的继承,因此其具有他们两个的功能
	// 序列化
	ChatInfo winfo = { "张三", 135246, { 2023, 4, 1 }, "晚上一起看电影吧" };
	stringstream oss;
	oss << winfo._name << endl;
	oss << winfo._id << endl;
	oss << winfo._date << endl;
	oss << winfo._msg << endl;
    // oss.str() 转化为c字符串
	string str = oss.str();
	cout << "网络发送:" << str << endl;

	// 反序列化
	ChatInfo rInfo;
	stringstream iss(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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值