• C++学习记录——삼십이 C++IO流



    1、C++标准IO流

    C语言的printf和scanf无法很好的输入输出自定义类型,且还需要程序员自己确定类型,所以C++就引入了输入流和输出流,是设备和内存之间的沟通。

    在这里插入图片描述

    其实iostream是通过菱形继承实现的。cout,cerr,clog其实做得有时候没有区分

    int main()
    {
    	cout << "1111" << endl;
    	cerr << "2222" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    也都能打印出来。fstream和sstream是针对文件和字符串的,对应的就是fprintf/fscanf,fwrite/fread,sprintf/sscanf。C++IO流的特点就是面向对象以及能更好地支持自定义类对象的IO。

    istream和ostream是防止拷贝的。

    C++IO流的缓冲区和C语言的缓冲区会进行同步,比如用cout和printf混合着输出,也能输出。默认开着同步,也可以关闭同步增强效率。和这个接口有关

    在这里插入图片描述

    实现了迭代器的容器,通常就不需要重载IO流。

    	string str;
    	while (cin >> str)
    	{
    		cout << str << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样的写法,就可以持续写入字符串,想要停止,就用ctrl + z + 换行来停止,这样是将流对象提取到结束,如果是ctrl + c就是信号强杀进程。我们要知道此时cin >> str是在做什么,>>右边的数据会被获取,给到cin,如果有多个,那就获取多个,最终都流向cin,然后返回一个istream类型的对象,这个对象会被转为bool类型来进行判断,但是一些自定义类型不可以转,像string,而istream可以转是因为类里重载了一个bool()函数,支持自定义类型转内置类型。

    在之前的博客中,写到过单参数构造的类支持隐式类型转换

    class A
    {
    public:
    	A(int a)
    		:_a(a)
    	{}
    private:
    	int _a;
    };
    
    int main()
    {
    	A aa1 = 1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这里的内部实现其实是构造一个临时对象,然后用这个对象来拷贝构造aa1,如果是A&就不行了。这是内置类型转自定义类型,但是自定义转内置不可:int x = aa1。解决办法就是重载一个类型()函数。

    	operator int()
    	{
    		return _a;
    	}
    
    • 1
    • 2
    • 3
    • 4

    如果该函数前面加上explicit就不行了,不过在下面写int x = (int)aa1,强制转换一下也行。

    istream里的operator bool(),如果被设置成错误的信息,就返回false,否则返回true,错误的信息例如ctrl + Z + 换行。

    2、C++文件IO流

    1、二进制读写

    #include 
    using namespace std; 
    
    int main()
    {
    	ofstream ofs("F://test.txt");
    	ofs << "hello world";
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    默认是覆盖写,第二个模板参数默认是ios_base::openmode mode = ios_base::out,ios_base是ofstream和ifstream的父类,如果想要用其它写入模式

    在这里插入图片描述

    是用 | 来隔开的。

    int main()
    {
    	ofstream ofs("F://test.txt", ofstream::out | ofstream::app);
    	ofs << "hello world";
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    看一个读写都有的代码

    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, ofstream::out | ofstream::binary);
    		ofs.write((const char*)&info, sizeof(info));
    	}
    
    	void ReadBin(ServerInfo& info)
    	{
    		ifstream ifs(_filename, ofstream::in | ofstream:::binary);
    		ifs.read((char*)&info, sizeof(info));
    	}
    
    private:
    	string _filename; // 配置文件
    };
    
    int main()
    {
    	ServerInfo winfo = { "192.0.0.1", 80, {2023, 9, 4} };
    	string str;
    	cin >> str;
    	if (str == "二进制写")
    	{
    		ConfigManager cm("F://test.txt");
    		cm.WriteBin(winfo);
    	}
    	else if (str == "二进制读")
    	{
    		ServerInfo rinfo;
    		ConfigManager cm("F://test.txt");
    		cm.ReadBin(rinfo);
    		cout << rinfo._address << endl;
    		cout << rinfo._port << endl;
    		cout << rinfo._date << endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    二进制读写对象中不能用string,能读进去,但是会崩,而且如果输入的字符串比较长那基本上就是崩掉。

    二进制写时,写到文件的是string对象及里面指向空间的指针,程序结束,string自动析构,指针指向的空间销毁了,所以就没法读取,读到的就是野指针。

    二进制读写的优点就是简单易操作,缺点就是没法看到实体。

    2、文本读写

    C语言文本读写的本质是内存中任何类型都转成字符串来交互,C++封装运算符重载后就变得更高效了。

    	void WriteText(const ServerInfo& info)
    	{
    		ofstream ofs(_filename);
    		ofs << info._address << " " << info._port << " " << info._date;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    日期类中实现了对流插入流提取的重载,不仅ostream能使用,ofstream和osstream都可以用,因为使用了继承体系,ostream是它们的父类。istream也是如此。

    	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;
    		ifs >> info._date;
    	}
    	
    	else if (str == "文本写")
    	{
    		ConfigManager cm("F://test.txt");
    		cm.WriteText(winfo);
    	}
    	else if (str == "文本读")
    	{
    		ServerInfo rinfo;
    		ConfigManager cm("F://test.txt");
    		cm.ReadText(rinfo);
    		cout << rinfo._address << endl;
    		cout << rinfo._port << endl;
    		cout << rinfo._date << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    3、stringstream

    对应C语言的sprintf和sscanf。

    在这里插入图片描述

    就像最上面那个图中显示,ios_base是一个基类,ios是它的派生类,istream和ostream是ios的派生类,iostream是istream和ostream的组合,然后istream,ostream,iostream各有分支,stingstream继承于iostream,istringstream继承于istream,ostringstream继承于ostream。

    头文件是sstream,不是stringstream。

    int main()
    {
    	ostringstream oss;
    	oss << 100 << " ";
    	oss << 11.22 << " ";
    	oss << "hello  wolrd" << endl;
    	string str = oss.str();
    	cout << str << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以把流插入的内容转换成一个字符串,也可以提取出来。

    int main()
    {
    	ostringstream oss;
    	oss << 100 << " ";
    	oss << 11.22 << " ";
    	oss << "hello  wolrd" << endl;
    	string str = oss.str();
    	cout << str << endl;
    
    	istringstream iss(str);
    	int i;
    	double d;
    	string s;
    	iss >> i >> d >> s;
    	cout << i << endl;
    	cout << d << endl;
    	cout << s << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    iss后面的顺序也得和oss拿到的顺序一样,要不然会使得某些变量内容不对,但也能打印出来。oss和iss也可以换成stringstream的类型。

    这个功能主要用于序列化和反序列化

    struct ChatInfo
    {
    	string _name;
    	int _id;
    	string _msg;
    };
    
    int main()
    {
    	ChatInfo winfo = { "张三", 123456, "你好" };
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    像这样,我们就可以用ostringstream把内容放到一个字符串,然后发送给需要这些信息的人,别人收到后,就可以用istringstream来提取。不过插入的时候每个后面都加上换行或者空格,因为提取时默认以换行或者空格来分割。

    复杂的实际情况还有其它方法。

    C++IO流

    结束。

  • 相关阅读:
    Ubtunu排查磁盘空间是否已满—并清理的方式
    自动气象站和气象信息网络
    名词解析 — — 企业开发中的VO、DTO、BO、PO等
    JavaScript 数组
    三大零知识(ZK)证明系统全面深度分析:STARKs SNARKs Bulletproof
    数字货币回测准备:下载与清洗全量历史数据
    怎样正确进行全链路压测?
    Docker安装Nginx并修改Nginx配置文件
    【MySQL】表的约束——空属性、默认值、列描述、zerofill、主键、自增长、唯一键、外键
    zookeeper第三章:项目案例
  • 原文地址:https://blog.csdn.net/kongqizyd146/article/details/132667280