• C++【IO流】


    一、C语言的输入和输出

    C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中。printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。
    注意宽度输出和精度输出控制。C语言借助了相应的缓冲区来进行输入与输出。如下图所示:
    在这里插入图片描述
    这里的设备除了终端或者是控制台之外,还可能是磁盘文件,或者是网卡。

    对输入输出缓冲区的理解:
    1.可以屏蔽掉低级I/O的实现,低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏
    蔽这部分的差异,可以很容易写出可移植的程序。
    2.可以使用这部分的内容实现“行”读取的行为,对于计算机而言是没有“行”这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一个“行”。

    printf/scanf
    fprintf/fscanf
    sprintf/sscanf

    int main()
    {
        int a=0;
        printf("%d",a);
        scanf("%d",&a);
        printf("%d\n",a);
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二、C++中的IO流

    在这里插入图片描述
    istream:流提取
    ostream:流输出
    因为提取和输出都要写一个类,非常麻烦,就写了一个特iostream,继承了这两个类的功能
    (但是因为这里存在菱形继承,所以非常复杂)

    自动类型识别

    int main()
    {
        int i=0;
        double j=2.2;
        cout<<i<<" "<<j<<endl;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    这是因为库底层都对其进行了函数的重载。

    istream和ostream可以更好地支撑自定义类型对象的流插入和流提取。
    自定义类型可以自己重载,控制流提取和流插入的方式。
    (string,日期类都可以进行流插入和流提取的重载)

    scanf和cin等输入,都是通过空格或者换行分隔开来的

    #include
    int main()
    {
        int year ,month ,day;
        //输入多个值默认输入是用空格或者换行分隔开来的。
        cin>>year>>month>>day;
        scanf("%d%d%d",&year,&month,&day);
        scanf("%d %d %d",&year,&month,&day);//不需要去加空格
        //如果日期是20221128这样输入的
        //我们可以用这种方式将我们的日期进行分隔
        scanf("%4d%2d%2d",&year,&month,&day);
        cout<<year<<" "<<month<<" "<<day<<endl;
    
        //因为C++不支持类似于我们上面c语言的这种写法,所以我们只能通过字符串切割来实现
        string str;
        cin>>str;
        year=stoi(str.substr(0,4));
        month=stoi(str.substr(4,2));
        day=stoi(str.substr(6,2));
        cout<<year<<" "<<month<<" "<<day<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    多行测试用例如何写输入

    int main()
    {
        int year ,month ,day;
        string str;
        //使用了operate bool
        //怎么终止?ctrl+c直接kill -9终止进程。
        //ctr+z给一个流结束的标志。
        while(cin>>str)
        {
            year=stoi(str.substr(0,4));
            month=stoi(str.substr(4,2));
            day=stoi(str.substr(6,2));
            cout<<year<<"年"<<month<<"月"<<day<<"日"<<endl;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    返回值是cin类型的对象。
    一般是整型指针,比较运算符表达式道德结果可以做逻辑条件判断,0就是假,非0就是真,自定义类型一般是不能做while的条件类型判断的,这里存在隐式类型转换
    想要让自定义类型能够进行转换,我们需要将其进行强制类型转换,但是一般用于类型转换的()被占用了,所以我们就用operator bool,将我们的类型转化成一个bool值,并将这个bool值进行返回。
    在这里插入图片描述
    这就类似于我们下面的将我们的自定义类型a赋值给内置类型int的过程。

    class A
    {
    public:
        A(int a)
            :_a(a)
        {}
    private:
        int _a;
    };
    
    int main()
    {
    	//内置类型转换成自定义类型
        //隐式类型转换
        A aa1=1;//用1构造A的临时对象,再拷贝构造aa1,优化后直接构造aa1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如何让我们的自定义类型转换成内置类型

    class A
    {
    public:
        A(int a)
                :_a(a)
        {}
    	
    	//重载运算符
        operator int()
        {
            return _a;
        }
    private:
        int _a;
    };
    
    int main()
    {
        //自定义类型转换成内置类型1
        int i=aa1;
        cout<<i<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    三、文件流

    ifstream读取文件

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    int main()
    {
        ifstream  ifs("/Users/Test.cpp");
        //读取一个字符
        char ch=ifs.get();
        while(ifs)
        {
            cout<<ch;
            ch=ifs.get();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    读取文件中不同类型的数据

    这是我们test.txt中的内容
    在这里插入图片描述

    可以更好地兼容自定义类型的读写

    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()
    {
        ifstream  ifs("/Users/test.txt");
        //可以使用c语言的方式从文件中读取不同类型的数据
    //    fscanf("%d%s%f",)
        //在c++中使用ifstream会方便一些
        int i;
        string s;
        double d;
        //对于自定义类型也是可以的
        //当然,可以的前提是日期类对象重载了流提取
        Date de;
        //流提取
        ifs>>i>>s>>d>>de;
        cout<<i<<endl;
        cout<<s<<endl;
        cout<<d<<endl;
        cout<<de<<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
    • 52
    • 53

    在这里插入图片描述

    二进制读写和文本读写

    二进制读写:就是文件在内存中是如何存储的,就怎样写到磁盘文件中
    文本读写:序列化(json,xml),将对象数据按照某种格式进行转换,序列化成字符串,写入磁盘当中。读取回来的也是字符串,反序列化,再转换成对象数据。

    二进制读写
    优点:快
    缺点:写出去的内容看不见
    (打开就大概率是乱码了,比方说存了一个100,但是二进制读写出来是100的补码)
    文本读写
    优点:可以看见写出去的是什么
    缺点:存在一个转换的过程,要慢一些

    二进制文件的写出

    #include 
    #include 
    using namespace std;
    struct ServerInfo
    {
        char _address[32];
        int _port;
    };
    
    struct ConfigureManager
    {
    public:
        ConfigureManager(const char* filename="./server.config")
            :_filename(filename)
        {}
    
        void WriteBin (const ServerInfo& info)
        {
            //默认的写都是覆盖的模式进行写入
            //但是app就是追加写,也就是append
            ofstream ofs(_filename,ios_base::out|ios_base::binary);
            //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
            ofs.write((char*)&info,sizeof(info));
        }
    private:
        string _filename;
    
    };
    int main()
    
    {
        ServerInfo info={"127.0.0.1",888};
        ConfigureManager cm;
        cm.WriteBin(info);
        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

    在这里插入图片描述
    这里我们的文本是不支持直接查看二进制文件的,所以我们这里的除了数字,别的我们文本的编码都是不可见的。

    二进制文件的读取

    #include 
    #include 
    using namespace std;
    struct ServerInfo
    {
        char _address[32];
        int _port;
    };
    
    struct ConfigureManager
    {
    public:
        ConfigureManager(const char* filename="./server.config")
            :_filename(filename)
        {}
    
    
        void ReadBin (const ServerInfo& info)
        {
            //默认的写都是覆盖的模式进行写入
            //但是app就是追加写,也就是append
            ifstream ifs(_filename,ios_base::in|ios_base::binary);
            //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
            ifs.read((char*)&info,sizeof(info));
        }
    private:
        string _filename;
    
    };
    int main()
    {
        //二进制的读取
        ServerInfo rinfo;
        ConfigureManager cm;
        cm.ReadBin(rinfo);
        cout<<rinfo._address<<endl;
        cout<<rinfo._port<<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

    在这里插入图片描述

    二进制读写的坑

    我们将这里的char*的address改成string,并且将这里的address的地址改成我们博客的连接

    #include 
    #include 
    using namespace std;
    struct ServerInfo
    {
        //char _address[32];
        string _address;
        int _port;
    };
    
    struct ConfigureManager
    {
    public:
        ConfigureManager(const char* filename="./server.config")
            :_filename(filename)
        {}
    
        void WriteBin (const ServerInfo& info)
        {
            //默认的写都是覆盖的模式进行写入
            //但是app就是追加写,也就是append
            ofstream ofs(_filename,ios_base::out|ios_base::binary);
            //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
            ofs.write((char*)&info,sizeof(info));
        }
    
        void ReadBin (const ServerInfo& info)
        {
            //默认的写都是覆盖的模式进行写入
            //但是app就是追加写,也就是append
            ifstream ifs(_filename,ios_base::in|ios_base::binary);
            //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
            ifs.read((char*)&info,sizeof(info));
        }
    private:
        string _filename;
    
    };
    int main()
    {
        //二进制写出去
    //    ServerInfo info={"https://editor.csdn.net/md/?articleId=128086656",888};
    //    ConfigureManager cm;
    //    cm.WriteBin(info);
        //二进制的读取
        ServerInfo rinfo;
        ConfigureManager cm;
        cm.ReadBin(rinfo);
        cout<<rinfo._address<<endl;
        cout<<rinfo._port<<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
    • 52

    我们先将数据写出去,然后重新读取进来。
    (如果我们的address比较短的话,是不会报下列的错误的,但是如果address比较长的话,就会报下面的错误)
    在这里插入图片描述

    在windows下的string,短的时候存的就·直接用共这里的_buff[16]将其进行存储,如果这里的字符串太长了,就用string中的_ptr指向这个string的地址。
    _buff[16]
    _ptr
    _size
    _capacity
    _port

        ServerInfo info={"https://editor.csdn.net/md/?articleId=128086656",888};
    
    • 1

    字符串是存在我们的堆上面的,我们将我们的字符串写出去,也就是调用write写出去的时候,调用的是这个字符串的地址。这里我们的是分两次运行的,也就是我们写二进制文件的时候,这个字符串的地址。当我们的读的进程结束之后,我们再运行读取的进程,我们将这个指针读取回来的时候,这个指针已经不是我们刚刚那个string的指针了!它已经是一个野指针了!!

    所以二进制读写的时候,一定要用数组,char[],不要使用string!!

    文本的方式写和读取

    #include 
    #include 
    using namespace std;
    struct ServerInfo
    {
        string _address;
        int _port;
    };
    
    struct ConfigureManager
    {
    public:
        ConfigureManager(const char* filename="./server.config")
            :_filename(filename)
        {}
    
        void WriteText(const ServerInfo& info)
    	{
    	//这里我们将指定的文件进行写的操作
    		ofstream ofs(_filename, ios_base::out);
    		//我们需要传入一个写入的子串和这个字符串的大小
    		ofs.write(info._address.c_str(), info._address.size());
    		//然后我们可以用换行将我们的字符串和后面的内容进行分隔
    		//当然这里传入一个换行符的话,我们可以使用上面的write,或者是下面的这种简单形式的put
    		ofs.put('\n');
    		//将我们的_port,(原本是整型)转换成字符串的类型
    		const string str = to_string(info._port);
    		//然后将我们的_port的字符串输出,第一个人参数是字符串,第二个参数是字符串的大小
    		ofs.write(str.c_str(), str.size());
    	}
    
    	void ReadText(ServerInfo& info)
    	{
    	//读取指定的文件
    		ifstream ifs(_filename, ios_base::in);
    		//创建一个字符串缓冲区
    		char buff[128];
    		//读取一行,放入我们上面刚刚创建的buffer当中
    		ifs.getline(buff, 128);
    		//然后将我们的address中的内容赋值成buff
    		info._address = buff;
    		//然后我们再读取进来一行数据
    		ifs.getline(buff, 128);
    		//然后将我们的_port,因为它原本是整型,我们需要将其重新转换成整型然后赋值给_port
    		info._port = stoi(buff);
    	}
    private:
        string _filename;
    
    };
    int main()
    {
        //文本方式写出去
        ServerInfo info={"127.0.0.1",888};
        ConfigureManager cm;
        cm.WriteText(info);
        //文本方式读进来
        ServerInfo rinfo;
    //    ConfigureManager cm;
        cm.ReadText(rinfo);
        cout<<rinfo._address<<endl;
        cout<<rinfo._port<<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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    可以成功将文本写出,并且正常读取
    在这里插入图片描述
    可以读取到我们的文本中的内容

    在这里插入图片描述

    但是这样的的方式非常麻烦,我们需要在写入的时候手动成员的每一个参数转换成字符串类型,再读取的时候,还要将读取到的字符串转换成对应的类型放入我们的成员中。
    所以我们可以用C++给我们提供的文件流的读和写来帮助我们进行上述操作。

    文件流写和读(c++)

    #include 
    #include 
    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
    {
        string _address;
        int _port;
        Date date;
    };
    
    struct ConfigureManager
    {
    public:
        ConfigureManager(const char* filename="./server.config")
            :_filename(filename)
        {}
        void WriteText(const ServerInfo& info){
            ofstream  ofs(_filename,ios_base::out);
            //这里我们流插入一个成员,我们最好加一个换行或者空格
            //这样可以防止我们的流提取的时候,读取到的数据粘连在一起
            ofs<<info._address<<endl;
            ofs<<info._port<<endl;
            ofs<<info.date<<endl;
        }
        void ReadText (ServerInfo& info)
        {
            ifstream ifs(_filename,ios_base::in|ios_base::binary);
            ifs>>info._address>>info._port>>info.date;
        }
    private:
        string _filename;
    
    };
    int main()
    {
        //二进制写出去
    //    ServerInfo info={"127.0.0.1",888};
        ServerInfo info={"127.0.0.1",888,{2022,11,30}};
        ConfigureManager cm;
        cm.WriteText(info);
        //二进制的读取
        ServerInfo rinfo;
    //    ConfigureManager cm;
        cm.ReadText(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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    文本方式读写的文件是可以直接进行查看的

    在这里插入图片描述

    然后我们也是可以正常读取到的

    在这里插入图片描述

    四、istringstream

    序列化和反序列化

    ostringstream和istringstream

    #include 
    
    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()
    {
        //序列化
        ChatInfo winfo={"催逝员",114514,{2022,11,30},"啊哈哈哈哈,鸡汤来喽,哈哈哈"};
        //对应的是sprintf
        ostringstream oss;
        oss<<winfo._name<<endl;
        oss<<winfo._id<<endl;
        oss<<winfo._date<<endl;
        oss<<winfo._msg<<endl;
    
    	//将其转换成str,然后不同的数据类型流都会帮我们处理好
        string str=oss.str();
        cout<<str<<endl;
    
        //假设中间是网络传输str,另一端接收到了字符串的信息数据
        //网络中传输的也是字符流
    
        //反序列化
        ChatInfo rInfo;
        istringstream iss(str);
        //按照一定的格式打印出来,显示在这里
        iss>>rInfo._name;
        iss>>rInfo._id;
        iss>>rInfo._date;
        iss>>rInfo._msg;
    
        cout<<"------------------------------------------"<<endl;
        cout<<"姓名:"<<rInfo._name<<endl;
        cout<<"日期:"<<rInfo._date<<endl;
        cout<<"ID:"<<rInfo._id<<endl;
        cout<<"信息:"<<rInfo._msg<<endl;
    
        cout<<"------------------------------------------"<<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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    在这里插入图片描述

    stringstream

    在库中stringstream继承了istringstream和ostringstream的功能,所以直接调用这个stringstream就可以了

    我们可以将我们上面的代码修改如下

    #include 
    
    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()
    {
        //序列化
        ChatInfo winfo={"催逝员",114514,{2022,11,30},"啊哈哈哈哈,鸡汤来喽,哈哈哈"};
        //对应的是sprintf
        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;
        stringstream iss(str);
        //按照一定的格式打印出来,显示在这里
        iss>>rInfo._name;
        iss>>rInfo._id;
        iss>>rInfo._date;
        iss>>rInfo._msg;
    
        cout<<"------------------------------------------"<<endl;
        cout<<"姓名:"<<rInfo._name<<endl;
        cout<<"日期:"<<rInfo._date<<endl;
        cout<<"ID:"<<rInfo._id<<endl;
        cout<<"信息:"<<rInfo._msg<<endl;
    
        cout<<"------------------------------------------"<<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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    在这里插入图片描述

  • 相关阅读:
    全球顶尖开源项目相聚外滩大会,绘制国际化开源生态新蓝图
    论文阅读:SuMa++: Efficient LiDAR-based Semantic SLAM
    idea 无法识别vue3语法
    Leetcode581. 最短无序连续子数组
    FPGA基础协议三:SPI读取FLASH
    2.4G无线麦克风领夹麦一拖二_全双工_杰理JL6976M单芯片方案
    3288S Android11 适配红外遥控功能(超详细)
    操作系统(五)I/O设备
    PostgresSQL - 生成uuid
    创作者基金 11 月亮点
  • 原文地址:https://blog.csdn.net/weixin_62684026/article/details/128086656