STL之自定义缓冲区

来源:互联网 发布:python好学么 编辑:程序博客网 时间:2024/05/29 17:56

简介

流缓冲区是一种I/O缓冲区,接口由basic_streambuf定义。针对字符型别char和wchar,标准程序库分别提供预先定义好的流缓冲区(streambuf)和宽字符流缓冲区(wstreambuf)。尤其是在特殊通道上,各类可以当做基类。

  • 函数eback(),gptr()和egptr()构成了read(input)缓冲区的界面。
  • 函数pbase(),pptr(),epptr()构成了write(output)缓冲区的界面。

pbase()是指output stream缓冲区的起始位置。pptr()是当前写入位置。epptr()是output 缓冲区的结尾。pbase()至pptr()之间的序列字符已被写至相应的输出通道,但未被清空。

输出缓冲区

对于程序员和程序开发者来说,类basic_streambuf仅仅是发送(sent)和提取(extracted)字符的地方。通常有两个公共函数,用于写入字符。

int streambuf::sputc( int nCh );

int streambuf::sputn( const char* pch, int nCount );

函数sputc发生错误时,会返回traits_type::eof()。

成员函数sputc()可用来向缓冲区中写入一个字符,如果当时有一个空的改写位置,字符就会被复制到该位置上。之后,指向当前改写位置的那个指针就会加1。如果缓冲区是空的,就调用虚函数overflow()将output缓冲区的内容发送至对应的输出管道中。

所以,通过重载overflow()函数可以自定义streambuffer。

#include <IOSTREAM>#include <streambuf>#include <LOCALE>#include <CSTDIO>using namespace std;class outbuf : public std::streambuf{protected:virtual int_type overflow(int_type c){if(c != EOF){c = std::toupper(c ,getloc());if(putchar(c) == EOF){return EOF;}}return c;}};void main(){outbuf ob;std::ostream out(&ob);int num = 56;out << "56 十六进制数值: " << std::hex << std::showbase << num << endl;}
上面是一个没有缓冲区的demo。因为缓冲区为空,每输出一个字符都会调用一次overflow()函数,在overflow中直接将字符输出。

#include <iostream>#include <io.h>#include <streambuf>#include <cstdio>using namespace std;static const int bufferSize = 10;class outbuf : public std::streambuf{protected:char buffer[bufferSize];//定义一个数组表示缓冲区。public:outbuf(){setp(buffer ,buffer + bufferSize - 1);//设置streambuf对象的缓冲区;}virtual ~outbuf(){sync();//程序退出时,将数组中的残余数据输出至外部设备。}protected:int flushBuffer(){int num = pptr() - pbase();if(write(1 ,buffer , num) != num)//输出。{return EOF;}pbump(-num);//将当前写指针清空,指向缓冲区的开始位置。return num;}virtual int_type overflow(int_type c){/*注意setp(buffer ,buffer + bufferSize - 1),所以pptr()==epptr()的时候,(也就是调用underflow的时候),pptr()指向缓冲区的最后一个位置(数组的最后一个位置)。所以pptr()指向有效内存。*/if(c != EOF){*pptr() = c;pbump(1);//使当前位置指向数组最后一个元素之后。}if(flushBuffer() == EOF){return EOF;}return c;}virtual int sync(){if(flushBuffer() == EOF){return -1;}return 0;}};class fdostream : public std::ostream{protected:outbuf buf;public:fdostream(int fd):ostream(0){rdbuf(&buf);//设置streambuf对象。}};void main(){fdostream out(1);out << "51 hexadecimal: " << std::hex << std::showbase << 51 << endl;}
输入缓冲区

函数sgetc()和sbumpc()可以从streambuf中读取一个字符,不同之处是sbumpc()会使当前指针后移,而sgetc()仅返回当前字符。

如果缓冲区为空,就没有可用字符了。缓冲区必须重新补给。如果没有可用字符,函数sbumpc()会调用虚函数uflow(),而uflow()的默认行为是调用underflow(),移动“读取指针”。

需要说明一下的时,eback()返回缓冲区中有效数据的起始位置。epptr()返回缓冲区有效数据末端位置。pptr()返回当前读取位置。这里需要注意的是,eback()并不一定等于pptr()因为为了支持回退,eback()和pptr()之间存储已经读取过的字符而epptr()并不一定是缓冲区数组的最后一个元素,因为可能没有从输入设备读取这么多的数据。

在给出demo之前,由于输入缓冲区支持回退,也就是读取指针可以左移,读取之前读取过的字符。所以,在重写函数underflow自定义自己的streambuf时,也需要支持这种特性。

#include <iostream>#include <streambuf>#include <cstring>#include <io.h>using namespace std;static const int bufferSize = 10;static const int maxBackNum = 4;class inbuf : public std::streambuf{protected:char buffer[bufferSize];public:inbuf(){setg(buffer + maxBackNum ,buffer + maxBackNum ,buffer + maxBackNum);}protected:virtual int_type underflow(){if(gptr() < egptr()){return *gptr();}int numputback;numputback = gptr() - eback();if(numputback > maxBackNum)numputback = maxBackNum;memcpy(buffer + maxBackNum - numputback ,gptr() - numputback ,numputback);int num ;num = read(0  ,buffer + maxBackNum ,bufferSize - maxBackNum);if(num < 0)return EOF;setg(buffer + (maxBackNum - numputback) ,buffer + maxBackNum ,buffer + maxBackNum + num);return *gptr();}};void main(){inbuf ib;std::istream in(&ib);char c;for(int i = 1 ;i <= 20 ;i++){in.get(c);cout << c << flush;if(i == 8){in.unget();in.unget();}}cout << endl;}