C++的I/O(vc版)(五)
来源:互联网 发布:c语言bonus什么意思 编辑:程序博客网 时间:2024/05/23 22:32
具体的缓冲类有filebuf,stringbuf,由于filebuf的大部分工作都转交给了C流,不好分析,所以重点分析的还是stringbuf。
对于stringstream,我们最常用的语句就是:
string s="1.2 5.6 7 7 ";istringstream is(s);float f; is >> f;
上述语句,给出了stringbuf的基本形式:以string初始化;
首先,假设我们已经都了解了stringstream的基本用法:ostringstream:向string中写数据;istringstream:从string中读取数据;
首先我们来说明一下stringbuf中的六个指针:其实在basic_stingbuf<>中,input和output共用缓冲区,且read操作的字符永远包含在write操作的字符之内,即先write后read:
这六个指针明确了,大部分问题就解决了。
首先看一下数据成员:
typedef basic_stringbuf<_Elem,_Traits,_Alloc> _Myt;typedef _Alloc allocator_type;typedef basic_streambuf<_Elem,_Traits> _Mysb;typedef basic_string<_Elem,_Traits,_Alloc> _Mystr;typedef typename _Traits::int_type int_type;typedef typename _Traits::pos_type pos_type;typedef typename _Traits::off_type off_type;_Elem* _seekhigh;_Strstate _mystate;//stream stateallocator_type _alloc;
对于流缓冲类需要知道一些状态信息,来决定basic_stringbuf<>的行为,具体的状态信息如下:
enum{//constants for bits in stream state_Allocated=1,//set if character array storage has been allocated_Constant=2,//set if character array nonmutable_Noread=4,//set if character array cannot be read_Append=8,//set if all writes are qppends_Atend=16};//set if initial writes are appendstypedef int _Strstate; _Strstate _Getstate(ios_base::openmode _mode) {//convert open mode to stream state bits _Strstate _state=(_Strstate)0; if(!(_mode &ios_base::in)) _state|=_Noread; if(!(_mode&ios_base::out)) _state|=_Constant; if(_mdoe&ios_base::app) _state|=_Append; if(_mode&ios_base::ate) _state|=_Atend; return (_state); }
其中_Getstate()用来将openmode的信息,转化成自己所需要的状态信息。
初始化过程:
explicit basic_stringbuf(ios_base::openmode _mode=ios_base::in|ios_base::out){_Init(0,0,_Getstate(_mode));}explicit basic_stringbuf(const _Mystr& _str,ios_base::openmode _mode=ios_base::in|ios_base::out){_Init(_str.c_str(),_str.size(),_Getstate(_mode));}void _Init(const _Elem* _ptr,size_t _count,_Strstate _state){//initialize buffer to[_ptr,_ptr+_count),set state_seekhigh=0;_mystate=_state;if(_count!=0 &&(_mystate&(_Noread|_Constant))!=(_Noread|_Constant)){//buffer that be read or writer_Elem* _pnew=_alloc.allocate(_count);_Traits::copy(_pnew,_ptr,_count);_seekhigh=_pnew+_count;if(!(_mystate &_Noread))_Mysb::setg(_pnew,_pnew,_pnew+_count);//set up read bufferif(!(_mystate &_Constant)){//set up writer buffer,是追加模式还是重置模式 _Mysb::setp(_pnew,(_mystate&_Atend)?_pnew+_count:_pnew,_pnew+_count);if(_Mysb::gptr()==0)_Mysb::setg(_pnew,0,_pnew);}_mystate|=_Allocate;}}
其中的主要工作转交给了_init函数,首先复制ptr指向的数组的内容,然后此函数根据状态信息初始化六个指针;
然后是stringstream中特有的两个函数str()和str(newstr),str()用来返回流中的内容,如果流既可以读也可以写,返回所有而不只是input缓冲区中的,如果这可以读,则返回input区中的内容,具体实现参见下述代码,而str(newstr)用来将流中的数据重置为newstr;
_Mystr str() const{//return string copy of character arrayif(!(_mystate&_Constant)&&_Mysb::pptr()!=0){//writable,make string from write buffer_Mystr _str(_Mysb::pbase(),(_seekhigh<_Mysb::pptr()?_Mysb::pptr() :_seekhigh)-_Mysb::pbase());return (_str);}else if(!(_mystate& _Noread) &&_Myssb::gptr()!=0){//readable.make string from read buffer_Mystr _str(_Mysb::eback(),_Mysb::egptr()-_Mysb::eback());return (_str);}else{_Mystr _nul;return (_nul);}}void str(const _Mystr& _newstr){_Tidy();_Init(_newstr.c_str(),_newstr.size(),_mystate);}
下面进入正题,正如我们上一篇文章中讨论的那样,一个流的具体实现的关键环节在于那几个关键的虚函数,overflow,underflow,uflow,pbackfail,seekpos,seekoff:
virtual int_type overflow(int_type _meta=_Traits::eof()){if(_mystate &_Append &&_Mysb::pptr()!=0 &&_Mysb::pptr()<_seekhigh)_Mysb::setp(_Mysb::pbase(),_seekhigh,_mystate);if(_Traits::eq_int_type(_Traits::eof(),_meta))return (_Traits::not_eof(_meta));//EOF return success codeelse if(_Mysb::pptr()!=0 &&_Mysb::pptr()<_Mysb::epptr()){*_Mysb::_Pninc=_Traits::to_char_type(_meta);return (_meta);}else if(_mystate &_Constant)return (_Traits::eof());//array cannot write,failelse{//grow buffer and storesize_t _oldsize=_mysb::pptr()==0?0:_Mysb::epptr()-_Mysb::eback();size_t _newsize=_oldsize;size_t _inc=_newsize/2<_MINSIZE?_MINSIZE:_newsize/2;//grow by 0.5 _MINSIZE=32while(0<_inc &&INT_MAX-_inc<_newsize)_inc/=2;if(_inc==0)return(_Traits::eof());//buffer cannot grow,增长的极限_newsize+=_inc;//上述代码确定增长的大小//分配空间_Elem* _newptr=_alloc.allocate(_newsize);_Elem* _oldptr=_Mysb::eback();//先将原来的数据复制进新的缓冲区if(0<_oldsize)_Traits::copy(_newptr,_oldptr,_oldsize);//如果原来的缓冲区大小为0,根据状态信息设置指针位置if(_oldsize==0){//first growth,set up pointer_seekhigh=_newptr;_Mysb::setp(_newptr,_newptr+_newsize);if(_mystate &_Noread)_Mysb::setg(_newptr,0,_newptr);else_Mysb::setg(_newptr,_newptr,_newptr+1);}else{//一般情况下的指针位置设置_seekhigh=_newptr+(_seekhigh-_oldptr);_Mysb::setp(_newptr+(_Mysb::pbase()-_oldptr),_newptr + (_Mysb::pptr() - _oldptr),_newptr + _newsize);if(_mystate &_Noread)_Mysb::setg(_newptr,0,_newptr);else_Mysb::setg(_newptr,_newptr+(_Mysb::gptr()-_oldptr),_Mysb::pptr()+1);}//如果我们以前的内存是通过分配器分的,那么就再通过它删除,同时,这次的内存一定是分配器分的,所以//设置状态if(_mystate &_Allocated)_alloc.deallocate(_oldptr,_oldsize);_mystate|=_Allocated;//写入字符*_Mysb::_Pninc()=_Traits::to_char_type(_meta);return(_meta);}}
上述代码给出了overflow的实现,也就是当write缓冲区已满,我们应该怎么做?这里的实现是使缓冲区增长为原来的1.5倍。具体的代码
对于underflow和uflow:
virtual int_type underflow(){//不能读取得情况下,读取失败if(_Mysb::gptr()==0)return (_Traits::eof());//正常情况下,正常读取else if (_Mysb::gptr()<_Mysb::egptr())return(_Traits::to_int_type(*_Mysb::gptr()));//不能读取情况有下述几种情况:else if(_mystate & _Noread || _Mysb::pptr() == 0|| _Mysb::pptr() <= _Mysb::gptr()&& _seekhigh <= _Mysb::gptr())return (_Traits::eof());// can't read, failelse{//extend read buffer into written area//这才是underflow的菜,如果发现read区的结尾指针小于write区,那么把结尾指针移动write区的pptr()if(_seekhigh<_Mysb::pptr())_seekhigh=_Mysb::pptr();_Mysb::setg(_Mysb::eback(),_Mysb::gptr(),_seekhigh);return (_Traits::to_int_type(*_Mysb::gptr()));}}
下面是pbackfail:
virtual int_type pbackfail(int_type _meta=_Traits::eof()){//put an element back to streamif (_Mysb::gptr() == 0|| _Mysb::gptr() <= _Mysb::eback()|| !_Traits::eq_int_type(_Traits::eof(), _meta)&& !_Traits::eq(_Traits::to_char_type(_meta), _Mysb::gptr()[-1])&& _mystate & _Constant)return (_Traits::eof());//failelse{_Mysb::gbump(-1);if(!_Traits::eq_int_type(_Traits::eof(),_meta))*_Mysb::gptr()=_Traits::to_char_type(_mate);return (_Traits::not_eof(_meta));}}
最后是seekoff和seekpos,由于在stringbuf中两者的相似性,下面仅附seekoff的代码:
virtual pos_type seekoff(off_type _off,ios_base::seekdir _way,ios_base::openmode _mode=ios_base::in | ios_base::out){//首先更新_seekhighif(_Mysb::pptr()!=0 &&_seekhigh<_Mysb::pptr())_seekhigh=_Mysb::pptr();//update seekhigh//调整read区if(_mode&ios_base::in && _Mysb::gptr()!=0){//position within read bufferif(_way==ios_base::end)_off+=(off_type)(_Mysb::gptr()-_Mysb::eback());else if (_way == ios_base::cur&& (_mode & ios_base::out) == 0)_off += (off_type)(_Mysb::gptr() - _Mysb::eback());else if (_way != ios_base::beg)_off = _BADOFF;if (0 <= _off && _off <= _seekhigh - _Mysb::eback()){// change read position_Mysb::gbump((int)(_Mysb::eback() - _Mysb::gptr() + _off));if (_mode & ios_base::out && _Mysb::pptr() != 0)_Mysb::setp(_Mysb::pbase(), _Mysb::gptr(),_Mysb::epptr());// change write position to match}else_off = _BADOFF;}//调整write区else if (_mode & ios_base::out && _Mysb::pptr() != 0){// position within write bufferif (_way == ios_base::end)_off += (off_type)(_seekhigh - _Mysb::eback());else if (_way == ios_base::cur)_off += (off_type)(_Mysb::pptr() - _Mysb::eback());else if (_way != ios_base::beg)_off = _BADOFF;if (0 <= _off && _off <= _seekhigh - _Mysb::eback())_Mysb::pbump((int)(_Mysb::eback()- _Mysb::pptr() + _off));// change write positionelse_off = _BADOFF;}else if (_off != 0)_off = _BADOFF;// neither read nor write buffer selected, failreturn (pos_type(_off));}//seekpos 就不写了
至此,basic_stringbuf<>的关键部分就完成了。
对于针对是istringstream还是ostreamstring所用的缓冲区,对basic_stringbuf的openmode传递不同的参数即可。
- C++的I/O(vc版)(五)
- c++的I/O(vc版)(一)
- C++的I/O(vc版)(二)
- C++的I/O(vc版)(三)
- C++的I/O(vc版)(四)
- C++的I/O(vc版)(六)
- C++的I/O(vc版)(七)
- I/O流类库(五)
- (转)五种I/O 模式
- C#之I/O系统(五)
- 《操作系统》总结五(I/O管理)
- 《操作系统》总结五(I/O管理)
- SQLite学习(五) - 异步I/O的实现 sqlite3async.c
- C++(11)标准I/O库
- 【网络】(七)五种I/O模型--I/O复用 Select(一)
- Linux下的socket编程实践(五)设置套接字I/O超时的方案
- 标准I/O库与Unbuffered I/O(不带缓冲的I/O)函数
- Unix下共有五种I/O模型(转载)
- STL之生成全排列:next_permutation & prev_permutation
- Linux 进程间的通信(一)
- Eclipse安装不了AXIS2 Tool插件
- Android打包签名——生成keystore到完成签名
- Android 代码判断当前设备是否为模拟器
- C++的I/O(vc版)(五)
- 在myeclipse中启动tomcat出现的一些问题详解
- 远心镜头设计原理详细介绍
- java用For循环Map
- IOS学习笔记51--多线程编程之GCD
- 1041. Be Unique (20)-PAT
- VS2012警告未能加载包“Visual C++ package”
- QML基础——QML国际化(中文示例)
- 【POJ1182】食物链+思路+代码(较全)