c++的输入输出 [三](istringstream,ostringstream,stringstream)
写在最前面
我这篇博文是根据别人的博文整理而成的,所以出现和别人一样的地方是我摘抄的,我会注明。
这份笔记我分成了三章,分别讲下面的三个部分。
基础介绍
c++的输入输出分为三种:
1.基于控制台的I/O
2.基于文件的I/O
3.基于字符串的I/O
第三种 基于字符串的I/O
这部分参考 istringstream、ostringstream、stringstream 类介绍 . 以及 ostringstream的用法
在过去,我们使用的是原始的<stdio.h>中的接口,但是今天我想讲的是一个新的库<sstream>,这个库拥有拥有类型安全和不会溢出这样抢眼的特性,很有理由让我们使用它。下面我将详述。
头文件
#include<sstream>
作用
istringstream类用于执行C++风格的字符串流的输入操作。
ostringstream类用于执行C++风格的字符串流的输出操作。
strstream类同时可以支持C++风格的串流的输入输出操作。
为什么我们要学
如果你已习惯了<stdio.h>风格的转换,也许你首先会问:为什么要花额外的精力来学习基于<sstream>的类型转换呢?也许对下面一个简单的例子的回顾能够说服你。假设你想用sprintf()函数将一个变量从int类型转换到字符串类型。为了正确地完成这个任务,你必须确保证目标缓冲区有足够大空间以容纳转换完的字符串。此外,还必须使用正确的格式化符。如果使用了不正确的格式化符,会导致非预知的后果。下面是一个例子:
int n=10000; chars[10]; sprintf(s,”%d”,n);// s中的内容为“10000”
到目前为止看起来还不错。但是,对上面代码的一个微小的改变就会使程序崩溃:
int n=10000; char s[10]; sprintf(s,”%f”,n);// 看!错误的格式化符
在这种情况下,程序员错误地使用了%f格式化符来替代了%d。因此,s在调用完sprintf()后包含了一个不确定的字符串。要是能自动推导出正确的类型,那不是更好吗?
进入stringstream
由于n和s的类型在编译期就确定了,所以编译器拥有足够的信息来判断需要哪些转换。<sstream>库中声明的标准类就利用了这一点,自动选择所必需的转换。而且,转换结果保存在stringstream对象的内部缓冲中。你不必担心缓冲区溢出,因为这些对象会根据需要自动分配存储空间。
具体分析
istringstream类
描述:从流中提取数据,支持 >> 操作
这里字符串可以包括多个单词,单词之间使用空格分开
istringstream的构造函数原形: istringstream::istringstream(string str);
初始化:使用字符串进行初始化
istringstream istr("1 56.7"); istr.str("1 56.7");//把字符串"1 56.7"存入字符串流中
使用:我们可以使用分解点获取不同的数据,完成 字符串 到 其他类型 的转换
常用成员函数:
str():使istringstream对象返回一个string字符串
举例:把字符串类型的数据转换为其他类型
#include <iostream> #include <sstream> using namespace std; int main() { istringstream istr("1 56.7"); cout<<istr.str()<<endl;//直接输出字符串的数据 "1 56.7" string str = istr.str();//函数str()返回一个字符串 cout<<str<<endl; int n; double d; //以空格为界,把istringstream中数据取出,应进行类型转换 istr>>n;//第一个数为整型数据,输出1 istr>>d;//第二个数位浮点数,输出56.7 //假设换下存储类型 istr>>d;//istringstream第一个数要自动变成浮点型,输出仍为1 istr>>n;//istringstream第二个数要自动变成整型,有数字的阶段,输出为56 //测试输出 cout<<d<<endl; cout<<n<<endl; system("pause"); return 1; }
举例2:把一行字符串放入流中,单词以空格隔开。之后把一个个单词从流中依次读取到字符串
#include <iostream> #include <sstream> using namespace std; int main() { istringstream istr; string line,str; while (getline(cin,line))//从终端接收一行字符串,并放入字符串line中 { istr.str(line);//把line中的字符串存入字符串流中 while(istr >> str)//每次读取一个单词(以空格为界),存入str中 { cout<<str<<endl; } } system("pause"); return 1; }
ostringstream类
描述:把其他类型的数据写入流(往流中写入数据),支持<<操作
ostringstream的构造函数原形: ostringstream::ostringstream(string str);
初始化:使用字符串进行初始化
ostringstream ostr("1234"); ostr.str("1234");//把字符串"1234"存入字符串流中
举例:
#include <iostream> #include <sstream> using namespace std; int main() { //初始化输出字符串流ostr ostringstream ostr("1234"); cout<<ostr.str()<<endl;//输出1234 ostr.put('5');//字符5顶替了1的位置 cout<<ostr.str()<<endl;//输出5234 ostr<<"67";//字符串67替代了23的位置,输出5674 string str = ostr.str(); cout<<str<<endl; system("pause"); return 1; }
上面这个实例可能看起来有点奇怪,其实是不了解put的使用,看看下面这段代码应该就懂了。
/*---------------------------------------------------------------------------- *** 建议:在用put()方法时,先查看当前put pointer的值,防止误写 ----------------------------------------------------------------------------*/ long curPos = ostr2.tellp(); //返回当前插入的索引位置(即put pointer的值),从0开始 cout << "curPos = " << curPos << endl; ostr2.seekp(2); // 手动设置put pointer的值 ostr2.put('g'); // 在put pointer的位置上写入'g',并将put pointer指向下一个字符位置 cout << ostr2.str() << endl;
下面有个很实用很技巧的应用:
在类型转换中使用模板
你可以轻松地定义函数模板来将一个任意的类型转换到特定的目标类型。例如,需要将各种数字值,如int、long、double等等转换成字符串,要使用以一个string类型和一个任意值t为参数的to_string()函数。to_string()函数将t转换为字符串并写入result中。使用str()成员函数来获取流内部缓冲的一份拷贝:
template<class T> void to_string(string & result,const T& t) { ostringstream oss;//创建一个流 oss<<t;//把值传递如流中 result=oss.str();//获取转换后的字符转并将其写入result }
这样,你就可以轻松地将多种数值转换成字符串了:
to_string(s1,10.5);//double到string to_string(s2,123);//int到string to_string(s3,true);//bool到string
可以更进一步定义一个通用的转换模板,用于任意类型之间的转换。函数模板convert()含有两个模板参数out_type和in_value,功能是将in_value值转换成out_type类型:
template<class out_type,class in_value> out_type convert(const in_value & t) { stringstream stream; stream<<t;//向流中传值 out_type result;//这里存储转换结果 stream>>result;//向result中写入值 return result; }
这样使用convert():
double d; string salary; string s=”12.56”; d=convert<double>(s);//d等于12.56 salary=convert<string>(9000.0);//salary等于”9000”
stringstream类
描述:是对istringstream和ostringstream类的综合,支持<<, >>操作符,可以进行字符串到其它类型的快速转换
stringstream的构造函数原形如下: stringstream::stringstream(string str);
初始化:使用字符串进行初始化
stringstream str("1234"); str.str("1234");//把字符串"1234"存入字符串流中
作用:
stringstream通常是用来做数据转换的
将文件的所有数据一次性读入内存
举例1:基本数据类型变字符串
/*基本数据类型变字符串*/ #include <fstream> #include <iostream> #include <sstream> using namespace std; int main() { /*整型变字符串*/ int n = 10; string str; stringstream stream; stream << n; stream >> str; cout<<str<<endl; stream.clear();//多次使用stringstream,要先清空下,不能使用stream.str("");否则下面输出10 /*char* 变 string*/ char cStr[10] = "china"; stream << cStr; stream >> str; cout<<str<<endl; system("pause"); return 1; }
举例2:字符串变基本数据类型
/*字符串变基本数据类型*/ #include <fstream> #include <iostream> #include <sstream> using namespace std; int main() { /*字符串 变 double*/ double n; string str = "12.5"; stringstream stream; stream << str; stream >> n; cout<<n<<endl; stream.clear();//多次使用stringstream,要先清空下,不能使用stream.str(""); /*string 变 char* */ string str1 = "china"; char cStr[10]; stream << str1; stream >> cStr; cout<<cStr<<endl;//输出china system("pause"); return 1; }
注意:
#include <iostream> #include <sstream> using namespace std; int main(int argc,char *argv[]) { std::stringstream stream; string str; while(1) { //clear(),这个名字让很多人想当然地认为它会清除流的内容。 //实际上,它并不清空任何内容,它只是重置了流的状态标志而已! stream.clear(); // 去掉下面这行注释,清空stringstream的缓冲,每次循环内存消耗将不再增加! //stream.str(""); stream<<"sdfsdfdsfsadfsdafsdfsdgsdgsdgsadgdsgsdagasdgsdagsadgsdgsgdsagsadgs"; stream>>str; //测试输出每次循环,你的内存消耗增加了多少! cout<<"Size of stream = "<<stream.str().length()<<endl; system("PAUSE"); } system("PAUSE"); return EXIT_SUCCESS; }
由于stringstream构造函数会特别消耗内存,似乎不打算主动释放内存(或许是为了提高效率),但如果你要在程序中用同一个流,反复读写大量的数据,将会造成大量的内存消耗,因些这时候,需要适时地清除一下缓冲 (用 stream.str("") )。
另外不要企图用 stream.str().resize(0),或 stream.str().clear() 来清除缓冲,使用它们似乎可以让stringstream的内存消耗不要增长得那么快,但仍然不能达到清除stringstream缓冲的效果,内存的消耗还在缓慢的增长!,至于stream.flush(),则根本就起不到任何作用。