图片来源:《灵笼》特别篇截图
目录
方案一:继承 std::streambuf
详细解释查看 《C++标准库:第2版》 15.13章节
输出流(没有缓冲区)
在不考虑缓冲区的情况下,继承 std::streambuf
并重写两个函数 :
int_type overflow (int_type c)
:单字符输出std::streamsize xsputn (const char* s, std::streamsize num)
:多字符输出,非必要,不重写的话会多次调用overflow
替代
#include <iostream>
#include <streambuf>
#include <cstdio>
// for write():
#ifdef _MSC_VER
# include <io.h>
#else
# include <unistd.h>
#endif
class fdoutbuf : public std::streambuf {
protected:
int fd; // file descriptor
public:
// constructor
fdoutbuf (int _fd) : fd(_fd) {
}
protected:
// write one character
virtual int_type overflow (int_type c) {
if (c != EOF) {
char z = c;
if (write (fd, &z, 1) != 1) {
return EOF;
}
}
return c;
}
// write multiple characters
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);
}
};
输出流(有缓冲区)
考虑缓冲区的话,需要:
- 创建缓冲区,调用
void setp( char_type* pbeg, char_type* pend )
初始化 - 管理缓冲区的三个指针:pbase(putbase,缓冲区开始位置)、pptr(put pointer,当前写入位置)、epptr(end put pointer,指向缓冲区末尾的后一个位置)
- 缓冲区满或者析构时主动缓冲区数据刷出、实现
int sync ()
和type overflow (int_type c)
#include <cstdio>
#include <streambuf>
// for write():
#ifdef _MSC_VER
# include <io.h>
#else
# include <unistd.h>
#endif
class fdoutbuf : public std::streambuf {
protected:
static const int bufferSize = 1024; // size of data buffer
char buffer[bufferSize]; // data buffer
int fd; // file descriptor
public:
/* constructor
* - initialize data buffer
* - one character less to let the bufferSizeth character
* cause a call of overflow()
*/
fdoutbuf(int _fd) : fd(_fd) {
setp (buffer, buffer+(bufferSize-1));
}
/* destructor
* - flush data buffer
*/
virtual ~fdoutbuf() {
sync();
}
protected:
// flush the characters in the buffer
int flushBuffer () {
int num = pptr()-pbase();
if (write (fd, buffer, num) != num) {
return EOF;
}
pbump (-num); // reset put pointer accordingly
return num;
}
/* buffer full
* - write c and all previous characters
*/
virtual int_type overflow (int_type c) {
if (c != EOF) {
// insert character into the buffer
*pptr() = c;
pbump(1);
}
// flush the buffer
if (flushBuffer() == EOF) {
// ERROR
return EOF;
}
return c;
}
/* synchronize data with file/destination
* - flush the data in the buffer
*/
virtual int sync () {
if (flushBuffer() == EOF) {
// ERROR
return -1;
}
return 0;
}
};
class fdostream : public std::ostream {
protected:
fdoutbuf buf;
public:
fdostream (int fd) : std::ostream(0), buf(fd) {
rdbuf(&buf);
}
};
输入流
输入流需要考虑支持回退(basic_istream& unget()
),重写 virtual int_type underflow ()
函数用以从文件描述符中读取数据到缓冲区,缓冲区前 4 字节保存可回退数据
#include <cstdio>
#include <cstring>
#include <streambuf>
// for read():
#ifdef _MSC_VER
# include <io.h>
#else
# include <unistd.h>
#endif
class inbuf : public std::streambuf {
protected:
/* data buffer:
* - at most, four characters in putback area plus
* - at most, six characters in ordinary read buffer
*/
static const int bufferSize = 10; // size of the data buffer
char buffer[bufferSize]; // data buffer
public:
/* constructor
* - initialize empty data buffer
* - no putback area
* => force underflow()
*/
inbuf() {
setg (buffer+4, // beginning of putback area
buffer+4, // read position
buffer+4); // end position
}
protected:
// insert new characters into the buffer
virtual int_type underflow () {
// is read position before end of buffer?
if (gptr() < egptr()) {
return traits_type::to_int_type(*gptr());
}
/* process size of putback area
* - use number of characters read
* - but at most four
*/
int numPutback;
numPutback = gptr() - eback();
if (numPutback > 4) {
numPutback = 4;
}
/* copy up to four characters previously read into
* the putback buffer (area of first four characters)
*/
std::memmove (buffer+(4-numPutback), gptr()-numPutback,
numPutback);
// read new characters
int num;
num = read (0, buffer+4, bufferSize-4);
if (num <= 0) {
// ERROR or EOF
return EOF;
}
// reset buffer pointers
setg (buffer+(4-numPutback), // beginning of putback area
buffer+4, // read position
buffer+4+num); // end of buffer
// return next character
return traits_type::to_int_type(*gptr());
}
};
方案二:使用 __gnu_cxx::stdio_filebuf
stdio_filebuf
支持使用文件描述符或者C文件指针(std::__c_file *
即 FILE *
)构造,部分构造函数:
stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))
stdio_filebuf (std::__c_file *__f, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))
使用示例:
#include <iostream>
#include <unistd.h>
#include <ext/stdio_filebuf.h>
int main(int, char**) {
int fd = STDOUT_FILENO;
__gnu_cxx::stdio_filebuf<char> buf(fd, std::ios_base::out);
std::ostream out(&buf);
out << "test"
return 0;
}
注意:对于文件描述符,stdio_filebuf
在析构时会主动调用 close
;但对于 FILE *
则不会主动关闭,需要手动调用 fclose
方案三:使用 boost::iostreams::file_descriptor_source 和 boost::iostreams::file_descriptor_sink
boost 的 file_descriptor_source
和 file_descriptor_sink
可以使用文件描述符来构造,继而构造出 boost::iostreams::stream
,部分构造函数:
enum file_descriptor_flags {
never_close_handle,
close_handle
};
file_descriptor_source( int fd, file_descriptor_flags );
file_descriptor_sink( int fd, file_descriptor_flags );
其中 file_descriptor_source
只能从文件描述符读取数据,file_descriptor_sink
则只能写入数据,file_descriptor_flags
用以控制析构时是否自动关闭文件描述符
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
namespace io = boost::iostreams;
int main(int, char**) {
int fd = STDOUT_FILENO;
io::file_descriptor_sink fds(fd,io::never_close_handle);
io::stream<io::file_descriptor_sink> out(fds);
out << "test";
return 0;
}
参考:
__gnu_cxx::stdio_filebuf< _CharT, _Traits >(3) —— man page
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。