sstream是标准C++里面多么强大多么有用的东西啊,从纷繁的格式字符串处理中稍微解放出来吧~ ——ZwqXin.com
包含<sstream>,顺便加个使用名字空间std的声明,可以开始了。sstream里包括istringstream、ostringstream、stringstream、stringbuf和各宽字符版本的对象类型。更方便的格式化字符串处理,更安全的类型转换。
本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
原文地址:http://www.zwqxin.cn/archives/cpp/use-sstream.html
这天还是要做那个机器视觉的编程实验,涉及不少数据的提取和保存。也就是"硬盘文件->内存数据"、"内存数据->硬盘文件"这类IO。对我来说,一般会使用的是C时代的FILE* - fprintf - sprintf - sscanf - fread/fwrite,或者C++的fstream - read/write之类的,保存到文件的操作比较简单,那么就来说从文件读入数据了:
2 | 45.3 | 68.2 | 36.8 |
7 | 77.4 | 55.7 | 70.1 |
3 | 22.0 | 79.9 | 53.5 |
... | ... | ... | ... |
这样的文件,以前我常常这样做:
- char Buffer[50] = {0};
- ifstream infile; //ifstream infile(filename);
- infile.open(filename, ios::in); //
- int DATA1;
- float DATA2, DATA3, DATA4;
- char c;
- while(infile.get(c))
- {
- if(c==0x00 || c == '\n' || c =='\t')continue;
- else
- {
- infile.seekg(-1, ios::cur);
- infile.getline(Buffer, 50);
- sscanf(Buffer, "%d %f %f %f ",
- &DATA1, &DATA2, &DATA3, &DATA4);
- DATA1list.push_back(DATA1);
- DATA2list.push_back(DATA2);
- DATA3list.push_back(DATA3);
- DATA4list.push_back(DATA4);
- }
- }
- infile.close();
基本上就是字面的意思。但是,这个样子经常会出错,特别是格式化字符那里,又有多少人铭记double类型的格式化字符是%lf而不是%f呢……用sstream,就可以这样做:
- //
- ifstream infile;
- infile.open(filename, ios::in);
- int DATA1;
- float DATA2, DATA3, DATA4;
- stringstream sem;
- sem << infile.rdbuf();
- while(true)
- {
- sem >> DATA1;
- if(sem.eof())break;
- sem >> DATA2;
- sem >> DATA3;
- sem >> DATA4;
- DATA1list.push_back(DATA1);
- DATA2list.push_back(DATA2);
- DATA3list.push_back(DATA3);
- DATA4list.push_back(DATA4);
- }
- infile.close();
1. fstream对象的rdbuf()成员函数返回的是一个std::filebuf*指针,用以重定向流,结果就是文件的内容全部作为一个C++流,流向我们的stringstream对象里。
2.我们从该stringstream对象里从头取出各个数据到中间变量DATAi中,这可不是一个一个字符取哦,因为它是以空格或者\n,\t之类字符为分隔的,所以很方便;
3.同时,这里取出到DATA的也不是字符串,而确实就是连续一个int三个float哦,因为内部有安全的转换机制,所以不要再担心C那种格式化的问题了,无论是INT还是DOUBLE还是CHAR*都能做到。这是stringstream最强大之处。
4.判断“取”的动作结束的eof()为什么要在最后做了一次“无用功”后才判断呢?因为只有这样能判断。stringstream这时候扮演着一个乐施者,他不会去数自己包包里还剩下多少块波板糖,只会不停伸手入包里取出糖,而只有最后一次伸手入包包发现已经没糖了——所以才叫最后一次——才会醒觉:哦,原来我一贫如洗了。
基本上太强了。再举例,这次是保存数据到文件,虽是简单,但如果按我以前用fstream文件流的方法,效果比C的fwrite要糟糕:
- char Buffer[50] = {0};
- ofstream fileSave;
- fileSave.open(filename,ios::ate);
- for(int i = 0 ; i < Num; i++)
- {
- sprintf(Buffer, "%d %.2f %.2f %.2f ",
- DATA1[i], DATA2[i], DATA3[i], DATA4[i]);
- fileSave.write(Buffer,sizeof("00 00.00 00.00 00.00"));
- }
- fileSave.close;
问题在sizeof("00 00.00 00.00 00.00")这里,我没办法确定真实数据的大小。事实上原始数据某组是(3 21.52 63.31 22.22 1.36)或者(320 25.36 54.322 72.31 32.101)都会很麻烦——一般是尽量让这个size大点,不要让它把后面的吃了,但过小的话后面就会出现其他多余的字符,或者每组数据不对齐造成文件不雅观(用strlen()就是另话了)。用stringstream的话,我只想说:很文雅:
- //
- ofstream fileSave;
- fileSave.open(filename,ios::ate);
- ostringstream strStream;
- strStream.setf(ios::showpoint);
- strStream << std::setprecision(4);
- for(int i = 0 ; i < Num; i++)
- {
- strStream<<DATA1[i]<<" " <<DATA2[i]<< " "<<DATA3[i]<< " "<<DATA4[i]<<endl;
- fileSave.write( strStream.str().c_str(), strStream.str().size() );
- }
- fileSave.close;
setprecision(4)是有效数字位数,所以数据不会难看(当然小数位数也是可设定的),配合strStream<<DATA1[i]<<" " ……这样的“流”式输出,感觉太好了。最后write的参数就是strStream内容转为std::string后,其大小以及再转化为char*所得的底层字符串地址了。太好了。
我不太研究STD里的效率问题,所以我不能给出效率方面的建议,但是,即使效率有小小损失,抵得过它的方便吗?以前基本用fprint,sscanf现在想多用stringstream了;以前是char*用得多、MFC也用CString,但现在觉得标准C++的string其实也很可爱的;以前是atoi,atof,然后sprintf做f-to-char,i-to-char(a),现在知道stringstream才是这类转换的高手哈。
最后提下,清空一个stringstream对象(stringstream strs)里的内容时,单strs.clear()是不行的,要用strs.str("")这句才行哦。最后最后,写程序时写出的一段代码,给有缘人参考,多多指教:
- //把整个文件内容存储到单独一个字符串里:
- ofstream dfileSave;
- stringstream outstream;
- dfileSave.open("tResult.txt", ios::in | ios::app);
- std::filebuf *pFileBuffer = dfileSave.rdbuf();
- outstream << pFileBuffer;
- int filesize = pFileBuffer->pubseekoff(0, ios::end, ios::in);
- std::string sre = outstream.str(); //1
- const char *de = sre.c_str(); //2
- dfileSave.close();
- //ZwqXin: 拜拜~!
本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
原文地址:http://www.zwqxin.cn/archives/cpp/use-sstream.html