Straem缓冲区其接口由class basic_treambuf<>定义。C++对于文件的操作是通过fstream创建文件流来实现的,
它不支持文件描述符,对于这个问题可以通过缓冲区重定向封装文件描述符可以很好的解决,在linux中一切都是文件,
同样对其inux的设备终端,socket等等都可以看成对文件的操作所以通过自定义缓冲区都可以像操作io流一样实现对其他设备的操作,这确实很有魅力。
一、用户自定义Output缓冲区
Output缓冲区有散的pointer维护
1、pbase()是缓冲区的开始
2、pptr()是当前写入的位置
3、epptr()是缓冲区结尾
自定义不带缓冲功能的缓冲区只需要重写overflow()函数,overflow()的作用是当缓冲区满时将数据写入,以为不带缓冲
功能所以每发送一个字符就调用overflow()。
#include<streambuf>
#include<locale>
#include<cstdio>
class outbuf:public std::streambuf
{
protected:
virtual int_type overflow(int_type c){
if(c!=EOF){
c=std::toupper(c,getloc());//有时候程序欲行到次处无卡继续运行,可以把getloc取掉唔知道为什么
//getloc()无法运行下请大牛指点
if(std::putchar(c)==EOF){
return EOF;
}
}
return c;
}
};
要想支持国际化即宽窄字符可以用模版实现上述类
#include<streambuf>
#include<locale>
//#include<cstdio>
#include<iostream>
template<typename charT,
typename traits=std::char_traits<charT> >
class basic_outbuf:public std::basic_streambuf<charT,traits>
{
protected:
virtual typename traits::int_type
overflow(typename traits::int_type c){
if(!traits::eq_int_type(c,traits::eof())){
// c=std::toupper(c,this->getloc());
c=std::toupper(c);
char cc=std::use_facet<std::ctype<charT>>
(this->getloc()).narrow(c,'?');
if(std::putchar(cc)==EOF){
return traits::eof();
}
}
return traits::not_eof(c);
}
};
typedef basic_outbuf<char> outbuf;
typedef basic_outbuf<wchar_t> woutbuf;
int main()
{
outbuf ob;
std::ostream out(&ob);
out<<"31 hexadecimal: "<<std::hex<<31<<std::endl;
}
如果想让程序带缓功能则还需要重写flushBuffer
要想实现文件描述符只需要在overflow()中把getchat换成文件描述符操作
write当然在这实现可以先创建一个文件描述符将其封装在streambuf中
#include<iostream>
#include<cstdio>
#include<streambuf>
#include<unistd.h>
class outbuf:public std::streambuf
{
protected:
static const int buffersize=10;
char buffer[buffersize];
public:
outbuf(){
setp(buffer,buffer+(buffersize-1));
std::cerr<<"@setp"<<std::endl;
}
virtual ~outbuf(){
std::cerr<<"@~outbuf"<<std::endl;
sync();
}
protected:
int flushBuffer(){
std::cerr<<"@flushbuffer"<<std::endl;
int num=pptr()-pbase();
if(write(1,buffer,num)!=num){
return EOF;
std::cerr<<"@flushbuffer return EOF "<<num<<std::endl;
}
pbump(-num);
std::cerr<<"@flushbuffer return num "<<num<<std::endl;
return num;
}
virtual int_type overflow(int_type c){
if(c!=EOF){
*pptr()=c;
pbump(1);
std::cerr<<"@c!=EOF"<<std::endl;
}
if(flushBuffer()==EOF){
std::cerr<<"@flushbuffer()=EOF"<<std::endl;
return EOF;
}
return c;
std::cerr<<"@return c"<<std::endl;
}
virtual int sync(){
std::cerr<<"@sync"<<std::endl;
if(flushBuffer()==EOF){
std::cerr<<"@return -1"<<std::endl;
return -1;
}
std::cerr<<"@return 0"<<std::endl;
return 0;
}
};
int main()
{
outbuf buffer;
std::cerr<<"------------1"<<std::endl;
std::ostream os(&buffer);
std::cerr<<"----------------2"<<std::endl;
os<<"hello buffersize hello buffersize";
}
对于不带缓冲功能的缓冲区还有一种提高效率的方法就是重写sputn()它是一次写入多个字符
#include<iostream>
#include<streambuf>
#include<cstdio>
#ifdef _MSC_VER
#include<io.h>
#else
#include<unistd.h>
#endif
class fdoutbuf:public std::streambuf
{
protected:
int fd;
public:
fdoutbuf(int _fd):fd(_fd){}
protected:
virtual int_type overflow(int_type c){
if(c!=EOF){
char z=c;
if(write(fd,&z,1)!=1){
return EOF;
}
}
return c;
}
virtual std::streamsize xsputn(const char* s,std::streamsize num){
return write(fd,s,num);
}
};
class fdostream:public std::ostream
{
protected:
fdoutbuf buf;
public:
fdostream(int fd):std::ostream(0),buf(fd){
rdbuf(&buf);
}
};
int main()
{
fdostream out(1);
out<<"31 hexadecimal "<<std::hex<<31<<std::endl;
}
二自定义Input缓冲区
input机制与output机制基本相似,但由于input有可能不进行最后的读取中所,有一些后退动作比如unget(),所以
这意味这你需要覆盖更多的函数,当然如果不需要这些操作就可以忽略,比如socket没有返回上一字符这种操作,
无论如何自定义input比output要复杂,如果要定义不带缓冲功能的缓冲区则需要覆盖underflow()和uflow(),其中uflow()
默认行为是调用underflow并移动(前进),如果定义带缓冲功能的缓冲区则只需要覆盖underflow(),是的带缓冲功能的
实现起来要更简单,因为所谓带缓冲就是数组一次性的读取,无所谓移动,其中underflow的功能是负责读取数据;
这里我就只是先带缓冲的input了
#include<cstdio>
#include<cstring>
#include<streambuf>
#include<unistd.h>
#include<iostream>
#include<fcntl.h>
class inbuf:public std::streambuf
{
protected:
static const int buffersize=10;
char buffer[buffersize];
int fd;
public:
inbuf(int f):fd(f){
setg(buffer+4,
buffer+4,
buffer+4);
}
protected:
virtual int_type underflow(){
std::cerr<<"----------------"<<std::endl;
if(gptr()<egptr()){
return traits_type::to_int_type(*gptr());
}
int numPutback;
numPutback=gptr()-eback();
if(numPutback>4){
numPutback=4;
}
std::memmove(buffer+(4-numPutback),gptr()-numPutback,
numPutback);
int num;
num=read(fd,buffer+4,buffersize-4);
if(num<=0){
return EOF;
}
setg(buffer+(4-numPutback),
buffer+4,
buffer+4+num);
return traits_type::to_int_type(*gptr());
}
};
int main()
{
int fd=open("b.txt",O_RDONLY|O_WRONLY);
inbuf buffer(fd);
std::istream is(&buffer);
std::string s;
is>>s;
std::cout<<s;
std::cout<<is.rdbuf()<<std::endl;
}
最后,我想读者也许韩式该觉迷糊,因为我的叙述着实不给力,而且代码也乱的很,之所以这样是因为管有io流这块
实在有太多的内容,相互联系,所以我建议看一下《c++标准程序库》那里一一做了详细解释,除此之外要想来灵活运用建议自己实现比如file,socket的封装,建议看一下:
http://blog.csdn.net/turkeyzhou/article/details/8983379