• 【 C++ 】IO流


    目录

    1、C语言的输入输出

    2、流是什么

    3、C++IO流

          3.1、C++标准IO流

          3.2、C++文件IO流

                   文件操作步骤

                   以二进制的形式操作文件

                   以文本的形式操作文件

    4、stringstream的介绍


    1、C语言的输入输出

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

    • scanf():从标准输入设备(键盘)读取数据,并将值存放在变量中
    • printf():将指定的文字/字符串输出到标准输出设备(屏幕),使用时注意宽度输出和精度输出控制。

    C语言借助了相应的缓冲区来进行输入与输出,如下图所示:

    对输入输出缓冲区的理解:

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

    scanf与printf是和控制台、终端相关的,还有与文件相关的(fscanf、fprintf)

    • fscanf:针对所有输入流的格式化的输入语句 —— stdin/文件
    • fprintf:针对所有输出流的格式化输出语句 —— stdout/文件

    还有用来序列化字符串的(sscanf、sprintf)

    • sscanf:从一个字符串中读取一个格式化的数据
    • sprintf:把一个格式化的数据,转换成字符串

    上述是C语言管理字符串序列号、反序列化的方式,C++的流也是按照类似的方式实现的。


    2、流是什么

    • “流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据( 其单位可以是bit,byte,packet )的抽象描述。
    • C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。

    它的特性是:有序连续具有方向性。为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能


    3、C++IO流

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


    3.1、C++标准IO流

    C++标准库提供了4个全局流对象cin、cout、cerr、clog:

    1. 使用cout进行标准输出,即数据从内存流向控制台(显示器)
    2. 使用cin进行标准输入即数据通过键盘输入到程序中
    3. C++标准库还提供了cerr用来进行标准错误的输出
    4. 以及clog进行日志的输出

    从上图可以看出,cout、cerr、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同。

    注意:

    1、在使用时候必须要包含文件并引入std标准命名空间,或是在使用时指定cout和cin所属的命名空间。

    1. #include//包含iostream文件
    2. using namespace std;//引入std标准命名空间
    3. int main()
    4. {
    5. int a;
    6. cin >> a;
    7. cout << a << endl;
    8. std::cin >> a;//使用时指定所属命名空间
    9. std::cout << a << std::endl;//使用时指定所属命名空间
    10. return 0;
    11. }

    2、cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中提取,如果一次输入过多,则多余的数据会留在缓冲区以供之后提取,如果输入错了,必须在回车之前进行修改,回车键按下就无法进行修改了,只有把输入缓冲区的数据取完后,才会要求输入新的数据。

    • 例如:如下的代码,若在第一次输入时输入两个数字,中间以空格间隔开,空格后的数据就是多余的数据,会存留在缓冲区,等到下一次输入时,会直接从缓冲区提取前面多余的数据。
    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. int a, b, c;
    6. cin >> a;//输入10 20 30
    7. cout << a << endl;//10
    8. cin >> b;//直接从缓冲区提取数据
    9. cout << b << endl;//20
    10. cin >> c;//直接从缓冲区提取数据
    11. cout << c << endl;//30
    12. return 0;
    13. }

    3、输入的数据类型必须与要提取的数据类型一致,否则出错,出错只是在流的状态字state中对应位置(置1),程序继续。

    4、空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格。回车符也无法读入。

    • 例如:我们使用cin无法把含有空格字符的字符串"hello world"输入到string对象中。
    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. string str;
    7. cin >> str;//输入"hello world"
    8. cout << str << endl;//输出"hello"
    9. return 0;
    10. }
    • 对于含有空格的字符串,我们可以使用getline函数来解决此问题,因为getline函数只有遇到'\n'换行符才会停止。
    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. string str;
    7. getline(cin, str);//输入"hello world"
    8. cout << str << endl;//输出"hello world"
    9. return 0;
    10. }

    5、cin和cout可以直接输入和输出内置类型数据,因为标准库已经将所有内置类型的输入和输出全部重载了。

    • >>运算符重载:

    • <<运算符重载:

    6、对于自定义类型,如果要支持cin和cout的标准输入输出,则需要对<<和>>进行重载。

    • 如下简易实现的自定义类型日期类,支持了对cin和cout的重载,就可以直接进行cin输入和cout输出了
    1. #include
    2. using namespace std;
    3. class Date
    4. {
    5. //友元函数
    6. friend ostream& operator<<(ostream& out, const Date& d);//流插入 <<
    7. friend istream& operator>>(istream& in, Date& d);//流提取 >>
    8. public:
    9. Date(int year = 1, int month = 1, int day = 1)
    10. {
    11. _year = year;
    12. _month = month;
    13. _day = day;
    14. }
    15. private:
    16. int _year;
    17. int _month;
    18. int _day;
    19. };
    20. //流插入 <<
    21. ostream& operator<<(ostream& out, const Date& d)
    22. {
    23. out << d._year << "-" << d._month << "-" << d._day << endl;
    24. return out;
    25. }
    26. //流提取 >>
    27. istream& operator>>(istream& in, Date& d)
    28. {
    29. in >> d._year >> d._month >> d._day;
    30. return in;
    31. }
    32. int main()
    33. {
    34. Date d;
    35. cin >> d;//2022 11 20
    36. cout << d << endl;//2022-11-20
    37. }

    7、在线OJ中的输入和输出  &&  8、istream类型对象转换为逻辑条件判断值

    • 对于IO类型的算法,一般都需要循环输入,对于C语言,我们是这样写的:
    1. int main()
    2. {
    3. char buff[128];
    4. while (scanf("%s", buff) != EOF)
    5. {
    6. //……
    7. }
    8. return 0;
    9. }

    一旦程序运行,就会陷入循环,如果我们想要结束此循环,在C语言中我们通常有两种方法:

    1. ctrl + z 加 换行
    2. ctrl + c(强制杀掉进程)

    在C++中,要想循环输入,我们是这样解决的:

    1. int main()
    2. {
    3. string str;
    4. while (cin >> str)
    5. {
    6. cout << str << endl;
    7. }
    8. return 0;
    9. }

    对于C++,我想要终止此循环,该如何解决的呢?

    首先,我cin>>str实际上调用的是string类中的operator>>(cin, str)运算符重载,相当于是拿此运算符重载的返回值来解决何时终止循环的问题。来看下库里的整体框架:

    由此可见,此函数的返回值就是cin,也就是istream类型的对象,可istream类型的对象(cin)本身不能作为这里循环的条件逻辑判断,但是C++支持一种语法,可以支持自定义类型的隐式类型转换,在讲解此问题之前,先来看看如下的样例:

    • C++有一个运算符重载比较特殊,可以支持把自定义类型转换为内置类型,看如下的自定义类型日期类:
    1. class Date
    2. {
    3. friend ostream& operator << (ostream& out, const Date& d);
    4. friend istream& operator >> (istream& in, Date& d);
    5. public:
    6. Date(int year = 1, int month = 1, int day = 1)
    7. :_year(year)
    8. , _month(month)
    9. , _day(day)
    10. {}
    11. //支持Date对象转换为bool类型
    12. operator bool()
    13. {
    14. //这里是随意写的,假设输入_year为0,则结束
    15. if (_year == 0)
    16. return false;
    17. else
    18. return true;
    19. }
    20. //支持Date对象转换为int类型
    21. operator int()
    22. {
    23. return _year + _month + _day;
    24. }
    25. private:
    26. int _year;
    27. int _month;
    28. int _day;
    29. };
    30. int main()
    31. {
    32. //内置类型转自定义类型(隐式类型转换)
    33. Date d1 = 0;
    34. Date d2 = { 2022, 11, 20 };
    35. //自定义类型转换为内置类型(bool)
    36. bool ret1 = d1;
    37. bool ret2 = d2;
    38. cout << ret1 << endl;//0
    39. cout << ret2 << endl;//1
    40. if (d2)
    41. {
    42. cout << "operator ()" << endl;//发生隐式类型转换
    43. }
    44. //自定义类型转换为内置类型(int)
    45. int i = d2;
    46. cout << i << endl;//2053
    47. return 0;
    48. }

    C++对实现类似于下面的函数,就可以支持自定义类型转为内置类型

    1. operator bool(){}
    2. operator int(){}
    • 转换过程如下:先调用operator bool()与operator int()的重载函数,调用函数之后返回值产生的一个临时变量,然后再赋值给另一变量,而这个临时变量我们是可以自己定义的,以及函数的逻辑也是我们定义的。
    • 上述main函数内部的if条件中,d2会进行隐式类型转换,转换成调用operator bool()函数,内部返回的是true。

    所以 为什么 (cin>>str 可以做判断的条件呢?)

    • cin>>str实际上调用的是string类中的operator>>(cin, str)运算符重载,此函数返回的是istream类型的对象(cin),既然它能做条件判断,那么istream内部必定是调用了operator bool()运算符重载函数,来看看库里的截图:

    • 由此可见,实际上operator bool()运算符重载是ios基类实现的,不过istream类继承了ios类,自然能够调用父类ios的成员函数,所以这里cin >> str(cin. operator bool())的返回值就可以作为循环结束的条件判断了。

    总结:

    • 实际上我们看到使用while(cin>>i)去流中提取对象数据时,调用的是operator>>,返回值是istream类型的对象,那么这里可以做逻辑条件值,源自于istream的对象又调用了operator bool,operator bool调用时如果接收流失败,或者有结束标志,则返回false。

    3.2、C++文件IO流

     下面展开讨论。


    文件操作步骤

    C++根据文件内容的数据格式分为二进制文件和文本文件。采用文件流对象操作文件的一般步骤如下:

    1、定义一个文件流对象

    对应操作场景
    ifstream只读、只输入用
    ofstream只写、只输出用
    fstream读 + 写、既输入又输出用

    2、使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系。文件常见的打开方式如下:

    打开方式功能
    in以读的方式打开文件
    out以写的方式打开文件
    binary以二进制的方式对文件进行操作
    ate输出位置从文件的末尾开始
    app以追加的方式对文件进行写入
    trunc先将文件内容清空再打开文件

    3、使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写,其中对文件进行提取和插入操作的常用成员函数如下:

    成员函数功能
    put插入一个字符到文件
    write插入一段字符到文件
    get从文件提取字符
    read从文件提取多个字符
    tellg获取当前字符在文件当中的位置
    seekg设置对文件进行操作的位置
    >>运算符重载将数据形象地以“流”的形式进行输入
    <<运算符重载将数据形象地以“流”的形式进行输出

    4、关闭文件

    • 示例:我读一个文件(test.cpp)
    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. ifstream ifs("test.cpp");
    7. //法一:
    8. while (ifs)
    9. {
    10. char ch = ifs.get();
    11. cout << ch;
    12. }
    13. /*法二
    14. char ch;
    15. while (ifs >> ch)
    16. {
    17. cout << ch;这里读出的结果不换行,过滤掉了空格和换行
    18. }*/
    19. return 0;
    20. }

    再比如我要拷贝一份test.cpp文件生成copy.cpp

    1. int main()
    2. {
    3. ifstream ifs("test.cpp");
    4. ofstream ofs("copy.cpp");
    5. char ch = ifs.get();
    6. while (~ch)
    7. {
    8. ofs << ch;
    9. ch = ifs.get();
    10. }
    11. return 0;
    12. }

    以二进制的形式操作文件

    以二进制形式读对文件进行写入操作:

    1. //以二进制的形式对文件进行写入
    2. int main()
    3. {
    4. ofstream ofile; //定义文件流对象
    5. ofile.open("test.txt", ofstream::out | ofstream::binary); //以二进制写入的方式打开test.txt文件
    6. char array[] = "hello world";
    7. ofile.write(array, strlen(array)); //将array字符串写入文件
    8. ofile.put('#'); //将字符'#'写入文件
    9. ofile.close(); //关闭文件
    10. //此时test.txt文件仅有"hello world#"
    11. return 0;
    12. }

    以二进制的形式对文件进行读取操作:

    1. //以二进制的形式对文件进行读取
    2. int main()
    3. {
    4. ifstream ifile;//定义文件流对象
    5. ifile.open("test.txt", ofstream::in | ofstream::binary);//以二进制的读取的方式打开test.doc文件
    6. ifile.seekg(0, ifile.end);//跳转到文件末尾
    7. int len = ifile.tellg();//获取当前字符在文件当中的位置,既文件的字符总数
    8. ifile.seekg(0, ifile.beg);;//重新回到文件开头
    9. char array[100];
    10. ifile.read(array, len);//将文件当中的数据全部读取到字符串array当中
    11. cout << len << endl;
    12. cout << array << endl;
    13. ifile.close();//关闭文件
    14. return 0;
    15. }

    再比如我现在有一个ServerInfo的类

    1. struct ServerInfo
    2. {
    3. char _address[32];
    4. int _port;
    5. };

    现在想要把它写到文件,我们有两种方式:

    1. 二进制的方式
    2. 文本的方式

    我们先以二进制的方式写入:

    1. struct ServerInfo
    2. {
    3. const char _address[256] = "";
    4. int _port;
    5. };
    6. struct ConfigManager
    7. {
    8. public:
    9. ConfigManager(const char* filename)
    10. :_filename(filename)
    11. {}
    12. //以二进制写
    13. void WriteBin(const ServerInfo& info)
    14. {
    15. ofstream ofs(_filename, ios_base::out | ios_base::binary);
    16. ofs.write((const char*)&info, sizeof(info));
    17. }
    18. //以二进制读
    19. void ReadBin(ServerInfo& info)
    20. {
    21. ifstream ifs(_filename, ios_base::in | ios_base::binary);
    22. ifs.read((char*)&info, sizeof(info));
    23. }
    24. private:
    25. string _filename; // 配置文件
    26. };
    27. int main()
    28. {
    29. //以二进制写
    30. ServerInfo winfo = { "https://legacy.cplusplus.com/reference/ios/", 80 };
    31. ConfigManager cf_bin("test.bin");
    32. cf_bin.WriteBin(winfo);
    33. //以二进制读
    34. ServerInfo rbinfo;
    35. cf_bin.ReadBin(rbinfo);
    36. cout << rbinfo._address << " " << rbinfo._port << " ";//https://legacy.cplusplus.com/reference/ios/ 80
    37. return 0;
    38. }

    以文本的形式操作文件

    以文本的形式对文件进行写入操作:

    1. //以文本的形式对文件进行写入
    2. int main()
    3. {
    4. ofstream ofile;//定义文件流对象
    5. ofile.open("test.txt");//以写入的方式打开test.txt文件
    6. char array[] = "hello world";
    7. ofile.write(array, strlen(array));//将array字符串写入文件
    8. ofile.put('#');//将字符'#'写入文件
    9. ofile.close();//关闭文件
    10. return 0;
    11. }

    以文本的形式对文件进行读取操作:

    1. //以文本的形式对文件进行读取
    2. int main()
    3. {
    4. ifstream ifile;//定义文件流对象
    5. ifile.open("test.txt");//以读取的方式打开test.txt文件
    6. ifile.seekg(0, ifile.end);//跳转到文件末尾
    7. int len = ifile.tellg();//获取当前字符在文件中的位置,即文件的字符总数
    8. ifile.seekg(0, ifile.beg);//重新回到文件开头
    9. char array[100];
    10. ifile.read(array, len);//将文件中的数据全部读取到字符串data中
    11. cout << len << endl;
    12. cout << array << endl;
    13. ifile.close();//关闭文件
    14. return 0;
    15. }

    再比如下面的类,并将其改成以文本的方式读取和写入:

    1. class Date
    2. {
    3. //友元函数
    4. friend ostream& operator<<(ostream& out, const Date& d);//流插入 <<
    5. friend istream& operator>>(istream& in, Date& d);//流提取 >>
    6. public:
    7. Date(int year = 1, int month = 1, int day = 1)
    8. {
    9. _year = year;
    10. _month = month;
    11. _day = day;
    12. }
    13. private:
    14. int _year;
    15. int _month;
    16. int _day;
    17. };
    18. //流插入 <<
    19. ostream& operator<<(ostream& out, const Date& d)
    20. {
    21. out << d._year << "-" << d._month << "-" << d._day << endl;
    22. return out;
    23. }
    24. //流提取 >>
    25. istream& operator>>(istream& in, Date& d)
    26. {
    27. in >> d._year >> d._month >> d._day;
    28. return in;
    29. }
    30. struct ServerInfo
    31. {
    32. string _address;
    33. int _port;
    34. Date _d;
    35. };
    36. struct ConfigManager
    37. {
    38. public:
    39. ConfigManager(const char* filename)
    40. :_filename(filename)
    41. {}
    42. //以文本方式写
    43. void WriteText(const ServerInfo& info)
    44. {
    45. ofstream ofs(_filename);
    46. ofs << info._address << endl;
    47. ofs << info._port << endl;
    48. ofs << info._d << endl;
    49. }
    50. //以文本方式读
    51. void ReadText(ServerInfo& info)
    52. {
    53. ifstream ifs(_filename);
    54. ifs >> info._address;
    55. ifs >> info._port;
    56. ifs >> info._d;
    57. }
    58. private:
    59. string _filename; // 配置文件
    60. };
    61. int main()
    62. {
    63. ServerInfo winfo = { "https://legacy.cplusplus.com/reference/ios/", 80, { 2022, 11, 22 } };
    64. //以文本方式写
    65. ConfigManager cf_txt("test.txt");
    66. cf_txt.WriteText(winfo);
    67. //以文本方式读
    68. ConfigManager cf_text("test.txt");
    69. ServerInfo rtinfo;
    70. cf_text.ReadText(rtinfo);
    71. cout << rtinfo._address << " " << rtinfo._port << " " << rtinfo._d << endl;//https://legacy.cplusplus.com/reference/ios/ 80 2022--11--22
    72. return 0;
    73. }

    注意:

    • C++文件流的优势就是可以对内置类型和自定义类型,都使用一样的方式,去流插入和流提取数据,当然这里自定义类型Date需要重载>> 和 <<

    4、stringstream的介绍

    在C语言中,如果想要将一个整形变量的数据转化为字符串格式,有如下两种方法:

    • 1、使用itoa()函数
    1. int main()
    2. {
    3. int n = 123456789;
    4. char s1[32];
    5. _itoa(n, s1, 10);//将整型的a转化为十进制字符数字存储在字符串s1中
    6. //_itoa(n, s1, 2);以二进制形式存在字符串s1中
    7. return 0;
    8. }
    • 2、使用sprintf函数
    1. int main()
    2. {
    3. int n = 123456789;
    4. char s2[32];
    5. sprintf(s2, "%d", n);//将整型的n转化为字符串格式存储在字符串s2当中
    6. return 0;
    7. }
    • 虽然上述俩函数都能够完成整型到字符串的转化,但是两个函数在转化时,都得需要先给出保存结果的空间,那空间要给多大呢,就不太好界定,而且转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃。

    在C++中,我们可以使用stringstream类对象来避开此问题。在程序中如果想要使用stringstream,必须要包含头文件。在该头文件下,标准库有如下三个类:

    对应操作场景
    ostringstream输出操作
    istringstream输入操作
    stringstream输入操作 + 输出操作

    来看这样一幅图:

    • 上图足矣说明,istringstream继承了istream,ostringstream继承了ostream,而iostream继承了istream和ostream,库里相当于是完成了一个菱形继承。stringstream是istringstream和ostringstream两个功能的合体。本文主要介绍stringstream,其作用如下:

    一、将数值类型数据格式化为字符串

    • ostringstream把数据类型转化为字符串
    • istringstream把字符串转化为数据类型
    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. //ostringstream把数据类型转为字符串
    7. int i = 123;
    8. ostringstream oss;
    9. oss << i;//把i流插入到oss
    10. string stri = oss.str();
    11. oss.str("");//将ostringstream底层管理的string对象设置为""。
    12. s.clear(); //将上次转换状态清空掉
    13. double d = 44.55;
    14. oss << d;//把d流插入到oss
    15. string strd = oss.str();
    16. //istringstream把字符串转为数据类型
    17. istringstream iss(strd);
    18. double n;
    19. iss >> n;//n = 44.55
    20. return 0;
    21. }
    • stringstream综合istringstream和ostringstream的功能
    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. int main()
    6. {
    7. int n = 10;
    8. string str;
    9. stringstream ss;
    10. ss << n; //将int类型的n放入输入流
    11. ss >> str; //从ss中抽取前面插入的int类型的值,赋值给string类型(方式一)
    12. cout << str << endl;//10
    13. ss.str(""); //将stringstream底层管理的string对象设置为""。
    14. ss.clear(); //将上次转换状态清空掉
    15. //进行下一次转换
    16. double b = 44.55;
    17. ss << b;
    18. str = ss.str(); //获取stringstream中管理的string类型(方式二)
    19. cout << str << endl;//44.55
    20. return 0;
    21. }

    注意:

    1. clear():注意多次转换时,必须使用clear将上次转换状态清空掉,stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit,因此下一次转换是必须调用clear()将状态重置为goodbit才可以转换,但是clear()不会将stringstreams底层字符串清空掉。
    2. s.str(""):将stringstream底层管理string对象设置成"",否则多次转换时,会将结果全部累积在底层string对象中

    二、字符串拼接

    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. string str;
    7. stringstream sstream;
    8. // 将多个字符串放入 sstream 中
    9. sstream << "hello" << " " << "world";
    10. sstream << " !!!";
    11. sstream >> str;
    12. cout << str << endl;//hello
    13. cout << "strResult is: " << sstream.str() << endl;//strResult is: hello world !!!
    14. // 清空 sstream
    15. sstream.str("");
    16. sstream.clear();
    17. sstream << "third string";
    18. str = sstream.str();
    19. cout << str << endl;//third string
    20. cout << "After clear, strResult is: " << sstream.str() << endl;//After clear, strResult is: third string
    21. return 0;
    22. }

    三、序列化和反序列化结构数据

    • 序列化:把一些值(整型、浮点型、自定义类型……)的数据转为字符串
    • 反序列化:把字符串转化为整数

    这个和第一点差不多,示例如下:我们日常聊天中,假如要把如下的信息发送过去:

    1. struct ChatInfo
    2. {
    3. string _name; // 名字
    4. int _id; // id
    5. Date _date; // 时间
    6. string _msg; // 聊天信息
    7. };

    这里的信息是个结构体对象,若要把此结构体信息发送给你,就需要运用到序列化和反序列化:

    1. int main()
    2. {
    3. // 结构信息序列化为字符串
    4. ChatInfo winfo = { "张三", 135246, { 2022, 11, 24 }, "晚上一起看电影吧" };
    5. ostringstream oss;
    6. oss << winfo._name << " " << winfo._id << " " << winfo._date << " " << winfo._msg;//把结构体信息插入到oss流
    7. string str = oss.str();//转换成字符串
    8. cout << str << endl << endl;
    9. // 我们通过网络这个字符串发送给对象,实际信息相对更复杂,这里做简化
    10. // 字符串解析成结构信息
    11. ChatInfo rInfo;
    12. istringstream iss(str);
    13. iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg;
    14. cout << "-------------------------------------------------------" << endl;
    15. cout << "姓名:" << rInfo._name << "(" << rInfo._id << ") ";
    16. cout << rInfo._date << endl;
    17. cout << rInfo._name << ":>" << rInfo._msg << endl;
    18. cout << "-------------------------------------------------------" << endl;
    19. return 0;
    20. }

    总结:

    1. stringstream实际是在其底层维护了一个string类型的对象用来保存结果。
    2. 多次数据类型转化时,一定要用clear()来清空,才能正确转化,但clear()不会将stringstream底层的string对象清空。
    3. 可以使用s. str("")方法将底层string对象设置为""空字符串。
    4. 可以使用s.str()将让stringstream返回其底层的string对象。
    5. stringstream使用string类对象代替字符数组,可以避免缓冲区溢出的危险,而且其会对参数类型进行推演,不需要格式化控制,也不会出现格式化失败的风险,因此使用更方便,更安全。
  • 相关阅读:
    什么是工作流引擎
    814. 二叉树剪枝 : 简单递归运用题
    从零开始学习Dubbo6——控制端dubbo-admin
    【随笔】提高代码学习水平(以更高的视角看事物)
    Jmeter接口自动化测试操作流程
    Linux网络套接字之TCP网络程序
    linux平常总结
    【C++初阶】类与对象(一)
    开发中 — — 异常与日志处理
    彩虹易支付 9.27 最新版加订单查询 sy 更新版
  • 原文地址:https://blog.csdn.net/bit_zyx/article/details/127945634