« 索引顶点的VBO与多重纹理下的VBO水效果Ⅲ - 抖动波 »

不要犹豫了,用sstream吧

sstream是标准C++里面多么强大多么有用的东西啊,从纷繁的格式字符串处理中稍微解放出来吧~  ——ZwqXin.com

包含<sstream>,顺便加个使用名字空间std的声明,可以开始了。sstream里包括istringstream、ostringstream、stringstream、stringbuf和各宽字符版本的对象类型。更方便的格式化字符串处理,更安全的类型转换。

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/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
... ... ... ...

这样的文件,以前我常常这样做:

  1. char Buffer[50] = {0};
  2.  
  3. ifstream infile;    //ifstream infile(filename);
  4. infile.open(filename, ios::in);  //
  5.  
  6. int DATA1;
  7. float DATA2, DATA3, DATA4;
  8. char c;
  9. while(infile.get(c))
  10. {
  11.     if(c==0x00 || c == '\n'  || c =='\t')continue;
  12.     else
  13.     {
  14.     infile.seekg(-1, ios::cur);
  15.     infile.getline(Buffer, 50);
  16.     sscanf(Buffer, "%d  %f %f %f ",
  17.     &DATA1, &DATA2, &DATA3, &DATA4);
  18.  
  19.        DATA1list.push_back(DATA1); 
  20.        DATA2list.push_back(DATA2); 
  21.        DATA3list.push_back(DATA3); 
  22.        DATA4list.push_back(DATA4);
  23.     }
  24.  
  25. }
  26.  
  27. infile.close();

基本上就是字面的意思。但是,这个样子经常会出错,特别是格式化字符那里,又有多少人铭记double类型的格式化字符是%lf而不是%f呢……用sstream,就可以这样做:

  1. //
  2. ifstream infile;    
  3. infile.open(filename, ios::in); 
  4.  
  5.     int DATA1;
  6.     float DATA2, DATA3, DATA4;
  7.  
  8.     stringstream sem;
  9.     sem << infile.rdbuf();
  10.  
  11.       while(true)
  12.       {
  13.         sem >> DATA1;
  14.         if(sem.eof())break;
  15.  
  16.         sem >> DATA2;
  17.         sem >> DATA3;
  18.         sem >> DATA4;
  19.  
  20.         DATA1list.push_back(DATA1);
  21.         DATA2list.push_back(DATA2);
  22.         DATA3list.push_back(DATA3);
  23.         DATA4list.push_back(DATA4);
  24.       }
  25. infile.close();
  26.  

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要糟糕:

  1. char Buffer[50] = {0};
  2.  
  3. ofstream fileSave;
  4.    fileSave.open(filename,ios::ate);
  5.  
  6. for(int i = 0 ; i < Num; i++)
  7. {
  8.  sprintf(Buffer, "%d  %.2f %.2f %.2f ",
  9. DATA1[i], DATA2[i], DATA3[i], DATA4[i]);
  10.  
  11.  fileSave.write(Buffer,sizeof("00  00.00 00.00 00.00"));
  12. }
  13.   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的话,我只想说:很文雅:

  1. //
  2.    ofstream fileSave; 
  3.    fileSave.open(filename,ios::ate); 
  4.  
  5.     ostringstream strStream;
  6.  
  7.     strStream.setf(ios::showpoint);
  8.     strStream << std::setprecision(4);
  9.  
  10.    for(int i = 0 ; i < Num; i++) 
  11.   { 
  12.      strStream<<DATA1[i]<<"  " <<DATA2[i]<< "  "<<DATA3[i]<< "  "<<DATA4[i]<<endl;
  13.  
  14.      fileSave.write( strStream.str().c_str(), strStream.str().size() );
  15.   }
  16.  
  17.   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("")这句才行哦。最后最后,写程序时写出的一段代码,给有缘人参考,多多指教:

  1.  
  2. //把整个文件内容存储到单独一个字符串里: 
  3.     ofstream dfileSave; 
  4.     stringstream outstream; 
  5.   
  6.     dfileSave.open("tResult.txt",  ios::in | ios::app); 
  7.   
  8.     std::filebuf *pFileBuffer =  dfileSave.rdbuf(); 
  9.   
  10.     outstream << pFileBuffer; 
  11.   
  12.     int filesize = pFileBuffer->pubseekoff(0, ios::end, ios::in); 
  13.   
  14.     std::string sre = outstream.str(); //1 
  15.     const char *de = sre.c_str();  //2 
  16.   
  17.     dfileSave.close(); 
  18.  
  19. //ZwqXin: 拜拜~!

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/cpp/use-sstream.html

  • quote 1.tadvent
  • http://dantvt.spaces.live.com
  • 在前两个例子中用 stringstream 是不是有点多余
    直接 fstream 的 << >> 不就够了?也省去了在 streambuf 中的复制过程

    将文件内容存入 string:
    ifstream in("file.txt");
    string contents((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
    zwqxin 于 2009-10-5 18:30:31 回复
    可今天是吃法国料理呢.不过谢谢阁下给的建议.学习了~
  • 2009-10-5 14:00:51 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.