C++: streambuf
2020-11-26 本文已影响0人
圣地亚哥_SVIP
streambuf定义了对流缓冲区操作的接口,主要分为输出缓冲流和输入缓冲流。streambuf有两种用法,一是直接使用各个接口,二是继承并实现新的I/O channels。
1. 输出缓冲流:
- pbase(): put base,输出缓冲流的首指针。
- pptr(): put pointer,当前可写位置
- epptr(): end put pointer,输出缓冲流的尾指针+1,即one past the last character。
函数:
setp(char new_pbase, char new_epptr)**
注:设置pbase,pptr,epptr三个指针的位置:
- pbase()=new_pbase;
- pptr()=bew_pbase;
- epptr()=new_epptr;
pbump(int offset)
注: 移动pptr offset个位置。当我们写入num个数据,pbump(num)。
overflow
当缓冲区满时的操作;
pptr() == epptr()
2. 输入缓冲流:
- eback(): 指向输入缓冲流的首指针
- gptr(): 当前输入缓冲流的位置
- egptr(): 输入缓冲流的末尾
- gbump(): 移动gptr
- setg(): 设置eback/gptr/egptr的位置
underflow()
当无缓冲区数据可读时。
cond: gptr()=egptr()
setg
setg (eback_base,new_gptr, new_egptr)
分别设置:
- eback
- gptr
- egptr
3. 基于streambuf自定义一个输入输出缓冲流
prebufferstream.hpp:
/* Author: zhangwen1@unionpay.com */
/* Date: 2020-11-24 */
/*
* Provide Custom Streambufeer Example
*/
#ifndef PREBUFFERSTREAM_HPP
#define PREBUFFERSTREAM_HPP
#include <streambuf>
#include <cstdio>
#include <exception>
class prebuffer: public std::streambuf{
public:
typedef std::char_traits<char> traits_ty;
typedef traits_ty::int_type int_type;
typedef traits_ty::pos_type pos_type;
typedef traits_ty::off_type off_type;
private:
int m_mode;
int m_filedes;
static const int s_buf_size = 4092;
static const int s_pback_size = 4;
char buffer[s_buf_size];
void init(int fd, int om, bool throw_exception);
int flushoutput();
public:
prebuffer(int fd, int om, bool throw_exception=false);
prebuffer(const prebuffer&)=delete;
prebuffer& operator=(const prebuffer&)=delete;
prebuffer(prebuffer&&)=delete;
prebuffer& operator=(prebuffer&&)=delete;
~prebuffer(){close();}
virtual int_type overflow(int_type c) override;
virtual int_type underflow() override;
bool is_open() const { return m_filedes >= 0; }
void close();
virtual int sync() override;
std::string get_str(){
return std::string(buffer,this->pptr()-buffer);
}
};
class FileError: public std::exception{
const char* what(void) const throw(){
return "Invaild fd";
}
};
#endif
prebufferstream.cc:
#include "prebufferstream.hpp"
#include <iostream>
#include <unistd.h>
#include <cstring>
void prebuffer::init(int fd, int om, bool throw_exception){
m_filedes = fd;
if (m_filedes > 0){
m_mode = om;
}
if (throw_exception && !is_open()){
throw FileError();
}
setg(buffer+s_pback_size,
buffer+s_pback_size,
buffer+s_pback_size);
setp(buffer,
buffer+s_buf_size-1);
}
prebuffer::prebuffer(int fd, int om, bool throw_exception){
init(fd,om,throw_exception);
}
void prebuffer::close(){
if (is_open()){
this->sync();
m_mode = 0;
m_filedes = -1;
}
}
prebuffer::int_type prebuffer::underflow(){
if (gptr() < egptr()){
return static_cast<unsigned char>(*gptr());
}
int num_putback=0;
if (s_pback_size > 0){
num_putback = gptr()-eback();
if (num_putback > s_pback_size){
num_putback = s_pback_size;
}
std::memcpy(buffer+s_pback_size-num_putback,gptr()-num_putback,num_putback);
}
const int num = ::read(m_filedes,buffer+s_pback_size,s_buf_size-s_pback_size);
if (num <= 0){return EOF;}
setg(buffer+s_pback_size-num_putback,buffer+s_pback_size,buffer+s_pback_size+num);
return static_cast<unsigned char>(*gptr());
}
prebuffer::int_type prebuffer::overflow(int_type c){
std::cout<<"Overflow Happend"<<std::endl;
if (!(m_mode & std::ios::out) || !is_open()) return EOF;
if (c != EOF){
*pptr() = c;
pbump(1);
}
if (flushoutput() == EOF)
{
return -1;
}
return c;
}
int prebuffer::flushoutput(){
if (!(m_mode & std::ios::out) || !is_open()) return EOF;
size_t num = pptr()-pbase();
if (::write(m_filedes,buffer,num) == -1){
return EOF;
}
pbump(-num);
return num;
}
int prebuffer::sync(){
if (flushoutput() == EOF){
return -1;
}
return 0;
}
测试, main.cc:
#include "prebufferstream.hpp"
#include <fcntl.h>
#include <ostream>
#include <istream>
#include <unistd.h>
#include <iostream>
int main(int argc,char* argv[]){
std::string file_name1 = "test";
std::string file_name2 = "intest";
int fdout;
int fdin;
if ((fdout=open(file_name1.c_str(),O_RDWR | O_CREAT | O_APPEND)) < 0){
std::cout<<"ERROR Open File"<<std::endl;
}
if ((fdin=open(file_name2.c_str(),O_RDONLY | O_CREAT | O_APPEND)) < 0){
std::cout<<"ERROR Open File"<<std::endl;
}
prebuffer ot{fdout,std::ios::out};
prebuffer it{fdin,std::ios::in};
std::ostream dout(&ot);
std::istream dint(&it);
dout<<1<<2<<3<<4<<5<<"\n";
std::cout<<ot.get_str()<<std::endl;
ot.sync();
int line;
while (dint>>line){
std::cout<<line<<" ";
}
close(fdout);
close(fdin);
return 0;
}