C++自定义缓冲区streambuf

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















































  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
streambufC++中I/O操作的核心部分之一,它负责管理输入和输出缓冲区,同时提供一些读取和写入数据的方法,是C++流的基础组件之一。 streambuf类的一个常见用途是实现网络通信中的TCP流。TCP流是指通过TCP协议进行数据传输的数据流,其特点是有序、可靠、面向连接和全双工通信。下面我们来介绍一下如何使用streambuf实现TCP流的读写。 首先,我们需要创建一个socket连接并建立TCP连接,这个过程可以使用C++中的socket API来实现。接着,我们需要创建一个streambuf对象用于管理输入和输出缓冲区。然后我们可以将socket的输入输出流与streambuf对象关联起来,这样就可以使用streambuf提供的接口对输入输出进行管理了。 下面是一个简单的示例代码,展示了如何使用streambuf实现TCP流的读写: ```c++ #include <iostream> #include <sstream> #include <string> #include <cstring> #include <cstdlib> #include <cstdio> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> using namespace std; const int BUFFER_SIZE = 1024; int main(int argc, char *argv[]) { if (argc <= 2) { cout << "Usage: " << basename(argv[0]) << " ip_address port_number" << endl; return 1; } const char* ip = argv[1]; int port = atoi(argv[2]); struct sockaddr_in server_address; bzero(&server_address, sizeof(server_address)); server_address.sin_family = AF_INET; inet_pton(AF_INET, ip, &server_address.sin_addr); server_address.sin_port = htons(port); int sockfd = socket(PF_INET, SOCK_STREAM, 0); if (sockfd < 0) { cout << "socket error" << endl; return 1; } if (connect(sockfd, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { cout << "connect error" << endl; return 1; } char buffer[BUFFER_SIZE]; memset(buffer, '\0', BUFFER_SIZE); streambuf* read_buffer = new streambuf(); streambuf* write_buffer = new streambuf(); read_buffer->pubsetbuf(buffer, BUFFER_SIZE); write_buffer->pubsetbuf(buffer, BUFFER_SIZE); istream input(read_buffer); ostream output(write_buffer); while (true) { cout << "> "; string line; getline(cin, line); if (line == "quit") break; output << line << endl; output.flush(); memset(buffer, '\0', BUFFER_SIZE); int bytes_read = recv(sockfd, buffer, BUFFER_SIZE - 1, 0); if (bytes_read <= 0) { cout << "Connection closed by peer" << endl; break; } read_buffer->sputn(buffer, bytes_read); string response; getline(input, response); cout << response << endl; } close(sockfd); delete read_buffer; delete write_buffer; return 0; } ``` 在上面的代码中,我们使用了C++中的iostream库来进行输入输出操作。我们创建了两个streambuf对象分别用于管理读取和写入缓冲区。然后我们将这两个streambuf对象与输入和输出流关联起来,这样就可以使用iostream库提供的接口来进行输入输出了。 在主循环中,我们先从终端读取用户输入的数据,并将其写入到输出缓冲区中。接着我们调用output.flush()将输出缓冲区中的数据发送到socket中。然后我们从socket中读取数据,并将其写入到读取缓冲区中。最后,我们从读取缓冲区中读取数据,并输出到终端中。 需要注意的是,上面的代码只是一个简单的示例,实际应用中还需要考虑一些细节问题,比如错误处理、缓冲区溢出等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值