C++知识点 -- C++的IO流

C++知识点 – C++的IO流


一、C++标准IO流

在这里插入图片描述
C++系统实现了一个庞大的类库,其中以ios为基类,其他类都是直接或间接派生自ios类;

C++标准IO流提供了4个全局流对象,cin、cout、cerr、clog,其中最常用的就是cin和cout;
ostream/istream 更好支持自定义类型对象的流插入和流提取;
自定义类型,可以自己重载,控制流提取和流插入的方式;

1.多个输入数据

void test()
{
    
    
	int year, month, day;
	cin >> year >> month >> day;
	scanf("%d%d%d", &year, &month, &day);
	//scanf("%d %d %d", &year, &month, &day);//中间不需要加空格
}

cin和scanf输入多个值都是默认使用空格或者换行符分割的,中间不用加空格;

如果是20230112这种格式的输入:

void test()
{
    
    
	//如果是20230112这种格式
	int year, month, day;
	scanf("%4d%2d%2d", &year, &month, &day);//scanf可以用这种方式

	//c++就需要使用string的字符分割
	string str;
	cin >> str;
	year = stoi(str.substr(0, 4));
	month = stoi(str.substr(4, 2));
	day = stoi(str.substr(6, 2));
}

2.多行数据读入

c语言可以使用scanf() != EOF来读取;
c++的读取方式如下:

void test()
{
    
    
	int year, month, day;

	string str;
	while (cin >> str)
	{
    
    
		year = stoi(str.substr(0, 4));
		month = stoi(str.substr(4, 2));
		day = stoi(str.substr(6, 2));
	}
}

结束程序可以用ctrl + Z + 换行 或者 ctrl + c

原因是cin读取数据后还会返回bool值,cin返回布尔值是通过重载operator bool实现的;
当读到文件末尾或者错误时,会返回false;

在这里插入图片描述
在这里插入图片描述
这里涉及到隐式类型转换

class A
{
    
    
public:
	A(int a)
		: _a(a)
	{
    
    }

	operator int()//operator int支持了自定义类型转换成内置类型
	{
    
    
		return _a;
	}
private:
	int _a;
};

void test()
{
    
    
	//内置类型 转换为 自定义类型
	A a1 = 1; // 隐式类型转换, 先用1构造A临时对象,再拷贝构造,优化后直接调用构造

	//自定义类型 转换为 内置类型
	int i = a1; // 原本编译器是不支持的,但是A中重载了operator int 后,就支持自定义类型转换成内置类型
}

二、C++文件IO流

C++文件IO流中包含三个对象:
ifstream:只输入用
ofstream:只输出用
fstream:既能输入又能输出

1.ifstream读文件

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

void test()
{
    
    
	ifstream ifs("text.txt");//可以直接用文件路径构造ifstream对象,就直接打开文件了

	//ifstream ifs();
	//ifs.open("text.txt");//也可以构造空对象,再调用fopen打开文件

	int i;
	string str1, str2;
	Date d1;

	ifs >> i >> str1 >> d1 >> str2;
}

构造ifstream对象之后就可以打开文件进行读取了,ifstream对象支持流提取,从文件中直接提取对象;
使用ifstream的流提取,是继承于istream的;
这里c++的流提取支持直接提取到自定义类型(前提是自定义类型重载了流提取);

2.文件读写类

二进制读写文件

struct SeverInfo
{
    
    
	char _address[32];
	int _port;
};

class ConfigManager
{
    
    
public:
	ConfigManager(string filename = "sever.config")
		: _filename(filename)
	{
    
    }

	//二进制写
	void WriteBin(const SeverInfo* info)
	{
    
    
		ofstream ofs(_filename, ios_base::out | ios_base::binary);//二进制写
		ofs.write((char*)info, sizeof(SeverInfo));
	}

	//二进制读
	void ReadBin(SeverInfo& info)
	{
    
    
		ifstream ifs(_filename, ios_base::in | ios_base::binary); //二进制读
		ifs.read((char*)&info, sizeof(SeverInfo));
	}

private:
	string _filename;
};

void test()
{
    
    
	//二进制写文件
	SeverInfo winfo = {
    
     "127.0.1", 888 };
	ConfigManager cm;
	cm.WriteBin(&winfo);

	//二进制读文件
	SeverInfo rinfo;
	cm.ReadBin(rinfo);
	cout << rinfo._address << endl;
	cout << rinfo._port << endl;
}

二进制读写时,ifstream和ofstream对象的第二个参数应该使用in/out和binary的组合,多个状态组合中间用 | 分割;
在这里插入图片描述
如果将SeverInfo中的address从char* 换成string,就会出错,关系到c++的string的设计问题;
在这里插入图片描述
在这里插入图片描述
长字符串是存在指针处的,写完之后进程结束,指针释放,读的时候下一个进程给的ptr不是一个地址,就会出现读取错误;

所以说二进制不适合深拷贝的类型;

文本读写文件

struct SeverInfo
{
    
    
	//char _address[32];
	string _address;
	int _port;
};

class ConfigManager
{
    
    
public:
	ConfigManager(string filename = "sever.config")
		: _filename(filename)
	{
    
    }

	//文本写
	void WriteText(const SeverInfo* info)
	{
    
    
		ofstream ofs(_filename, ios_base::out);//二进制写
		ofs.write(info->_address.c_str(), info->_address.size());
		ofs.put('\n');
		const string str = to_string(info->_port);
		ofs.write(str.c_str(), str.size());
	}

	//文本读
	void ReadText(SeverInfo& info)
	{
    
    
		ifstream ifs(_filename, ios_base::in); //二进制读
		char buff[128];
		ifs.getline(buff, 128);
		info._address = buff;
		ifs.getline(buff, 128);
		info._port = stoi(buff);
	}

private:
	string _filename;
};

这是使用write和read进行读写,更加方便地方式是使用流插入和流提取进行读写

struct SeverInfo
{
    
    
	//char _address[32];
	string _address;
	int _port;
};

class ConfigManager
{
    
    
public:
	ConfigManager(string filename = "sever.config")
		: _filename(filename)
	{
    
    }

	void WriteText(const SeverInfo& info)
	{
    
    
		ofstream ofs(_filename, ios_base::out);
		ofs << info._address << endl;
		ofs << info._port << endl;
	}

	void ReadText(SeverInfo& info)
	{
    
    
		ifstream ifs(_filename, ios_base::in | ios_base::binary);
		ifs >> info._address >> info._port;
	}

private:
	string _filename;
};

三、stringstream

字符串流:完成字符串和其他类型之间的转换;

将提取到的信息转换为字符串存储到流中:

void test()
{
    
    
	// 序列化
	ChatInfo winfo = {
    
     "张三", 135246, {
    
     2022, 4, 10 }, "晚上一起看电影吧" };
	//ostringstream oss;
	stringstream oss; // stringstream既有ostringstream的功能,也有istringstream的功能
	oss << winfo._name << endl;
	oss << winfo._id << endl;
	oss << winfo._date << endl;
	oss << winfo._msg << endl;

	//可以通过oss.str拿到流中的字符串
	string str = oss.str();
	cout << str << endl;
}

将流中的字符串转为其他类型

void test()
{
    
    
	// 序列化
	ChatInfo winfo = {
    
     "张三", 135246, {
    
     2022, 4, 10 }, "晚上一起看电影吧" };
	//ostringstream oss;
	stringstream oss;
	oss << winfo._name << endl;
	oss << winfo._id << endl;
	oss << winfo._date << endl;
	oss << winfo._msg << endl;

	string str = oss.str();
	cout << str << endl;

	// 网络传输str,另一端接收到了字符串串信息数据

	// 反序列化
	ChatInfo rInfo;
	//istringstream iss(str);
	//使用提取到的str来构造strinfstream对象,拿到字符串
	stringstream iss(str);
	iss >> rInfo._name;
	iss >> rInfo._id;
	iss >> rInfo._date;
	iss >> rInfo._msg;

	cout << "----------------------------------" << endl;
	cout << rInfo._date << endl;
	cout << rInfo._name << "[" << rInfo._id << "]:>" << rInfo._msg << endl;
	cout << "----------------------------------" << endl;
}

在这里插入图片描述
stringstream的优势在于可以对自定义类型进行处理;

猜你喜欢

转载自blog.csdn.net/kissland96166/article/details/130596452