东阳的学习笔记
stream 并不负责实际的读写操作, 而是委托给 stream buffer 完成
从使用者的角度看 Stream 缓冲区
对于 stream 的使用者来说,class basic_streambuf 只是发送、提取字符的地方。
streambuf定义了两个public函数,用来写入字符:
- sputc© 将字符 c 送入 stream 缓冲区
如果发生错误,返回eof - sputn(s, n) 将字符序列 s 中的 n 个字符送入 stream 缓冲区
返回实际写出的字符数,不考虑字符串终止符
从 stream 缓冲区中读取字符的接口比较复杂。这是因为对输入而言,必须时时观察监视未耗用的字符;解析时,字符最好能被送回 stream 缓冲区,相应的函数如下:
- in_avail() 返回有效字符的下界
- sgetc() 返回当前字符,不耗用
- sbumpc() 返回当前字符并耗用它
- snextc() 耗用当前字符并
返回下一个字符 - sgetn(b, n) 读取n个字符,并将他们存储到缓冲区b
- sputbackc© 将字符c返回 stream 缓冲区
- sungetc() 退回至前一个字符
还有一些函数用来存取局部对象、改变位置、或影响缓冲区
- pubimbue(loc): 为 stream 缓冲区安装 locale loc
- getloc(): 返回当前的 locale对象
- pubseekpos(pos): 将当前位置重新设定为某绝对位置
- pubseekpos(pos, which): 同上,并可指定 I/O方向
- pubseekoff(offset, rpos): …
- pubseekoff(offset, rpos, which) : …
- pubsetbuf(b, n): 影响缓冲行为
Stream 缓冲区迭代器
针对无格式 I/O 使用stream成员函数的另一种做法就是采用 stream 缓冲区迭代器
- 符合input迭代器和 output 迭代器的规格
- 从 stream 缓冲区读取或者写入单一字符。这样一来就能将字符层级的 I/O 纳入 C++标准程序库的算法管理范围内了。
输出缓冲迭代器的成员函数:
- ostreambuf_iterator(ostream) : 为流ostream产生一个迭代器
- ostreambuf_iterator(buff_ptr):为buff_ptr指向的缓冲区产
生一个迭代器 - *iter:无操作,返回iter
- iter = c : 调用sputc(),对缓冲区写入字符
- ++iter/iter++ : 无操作,返回iter
- failed() : 判断输出流迭代器能够执行改写操作
input stream缓冲区迭代器
- 输入流缓冲区迭代器和流istream迭代器所有操作类似
- 成员函数equal()判断两个输入流缓冲区迭代器是否相等,当两个迭代器都是或者都不是end-of-stream迭代器时,相等;
各项操作:
| 成员函数 | 含义 |
|---|---|
istreambuf_iterator<char>() | 产生一个end-of-stream迭代器 |
istreambuf_iterator<char>(istream) | 为istream产生一个输入缓冲区迭代器,并可能掉头sgetc()获取第一个字符 |
istreambuf_iterator<char>(buff_ptr) | 为缓冲区buff_ptr产生一个输入缓冲迭代器,并调用sgetc()获取第一个字符 |
| *iter | 返回当前字符 |
| ++iter | 以sbumpc读取下一个字符,返回其位置 |
| iter++ | 以sbumpc读取下一个字符,返回一个迭代器 |
| equal()/ == | 判断两个迭代器相等 |
| != | 判断两个迭代器不等 |
说明:
- 从流的当前位置到结尾: 由两个迭代器定义:
istreambuf_iterator<char>(istream)表示开始,istreambuf_iterator<char>()表示结尾; - 不可能以istream_iterators建立一个子序列;
一个运用实例
/*
* charset2.cpp
*
* Created on: 2021年1月12日
* Author: san
*/
/*
* Stream 缓冲区迭代器: 针对“无格式I/O”使用 stream 成员函数的一种做法
*/
#include <iostream>
#include <iterator>
using namespace std;
int main()
{
// input stream buffer iterator for cin
istreambuf_iterator<char> inpos(cin);
// end-of-stream iterator
istreambuf_iterator<char> endpos;
// output stream buffer iterator for cout
ostreambuf_iterator<char> outpos(cout);
// while input iterator is valid
while (inpos != endpos)
{
*outpos = *inpos; // assign its value to the output iterator
++inpos;
++outpos;
}
}
自定义 Stream 缓冲区
缓冲区主要接口由三个指针构成。函数 eback()、pptr()、epptr()取得。他们的意义是:
- pbase() (
put base):是 output stream 缓冲区的开始。 - pptr()(
put pointer):是当前写入位置 - epptr() (
end put pointer):指 output 缓冲区的结尾,指向最后一个字符的下一个位置
pbase() 至 pptr()之间的序列字符(不包含pptr()所指)已被写至相应的输出通道,但尚未清空(flush)

成员函数 sputn() 可用来一次写入多个字符。这个函数把实际任务委派给虚函数 xsputn(),后者可针对多个字符做出更有效的操作。class basic_streambuf 中的 xsputn() 对每个字符调用 sputc()。因此改写 xsputn()并非必要。但通常一次写入多个字符比一次写入一个效率要高的多,因此sputn()可用来优化对字符的处理
一个缓冲区的例子(并未采取缓冲行动)
只重载了 overflow()
/*
* output1x.hpp
*
* Created on: 2021年1月12日
* Author: san
*/
#ifndef IO_OUTBUF1X_HPP_
#define IO_OUTBUF1X_HPP_
#include <streambuf>
#include <locale>
#include <cstdio>
/*
* 自定义缓冲区(没有采取缓冲行为)
* 重载overflow
*/
template <class charT, class traits = std::char_traits<charT> >
class basic_outbuf : public std::basic_streambuf<charT, traits> {
protected:
/* central output function
* - print characters in uppercase mode
*/
virtual typename traits::int_type overflow(typename traits::int_type c)
{
if (!traits::eq_int_type(c, traits::eof()))
{
// convert lowercase to upper case
c = std::toupper(c, getloc());
// and writer the character to the standard output
if ( putchar(c) == EOF )
{
return traits::eof();
}
}
return traits::not_eof(c);
}
};
// definition
typedef basic_outbuf<char> outbuf;
typedef basic_outbuf<wchar_t> woutbuf;
#endif /* IO_OUTBUF1X_HPP_ */
另一个缓冲区的例子
重载了 overflow(),同时实作了 xputn()
/*
* outbuf2.hpp
*
* Created on: 2021年1月13日
* Author: san
*/
#ifndef IO_OUTBUF2_HPP_
#define IO_OUTBUF2_HPP_
#include <iostream>
#include <streambuf>
#include <cstdio>
/*
* 没有使用setp()初始化,此时的write缓冲区不具备缓冲能力
*/
extern "C" {
int write(int fd, const char *buf, int num);
}
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 chars
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);
}
};
#endif /* IO_OUTBUF2_HPP_ */
上一个的改进版
如果要实作出具备缓冲能力的 stream 缓冲区,writer缓冲区必须以函数 setp()初始化。以下例子说明这一点。
/*
* outbuf3.hpp
*
* Created on: 2021年1月13日
* Author: san
*/
#ifndef IO_OUTBUF3_HPP_
#define IO_OUTBUF3_HPP_
/*
* 使用setp()初始化 write缓冲区
* 这样stream缓冲区才真的具备缓冲能力
*/
#include <cstdio>
#include <streambuf>
extern "C" {
int write(int, const char *, int);
}
class outbuf: public std::streambuf{
protected:
static const int bufferSize = 10; // size of data buffer
char buffer[bufferSize]; // data buffer
public:
// constructor
outbuf()
{
setp(buffer, buffer+(bufferSize - 1));
}
// destructor
virtual ~outbuf()
{
sync();
}
protected:
// flush the chars in the buffer
int flushBuffer()
{
int num = pptr() - pbase();
if (write(1, buffer, num) != num)
{
return EOF;
}
pbump(-num); // reset put pointer accordingly
return num;
}
/* buffer full
* - write c and all previous chars
*/
virtual int_type overflow(int_type c)
{
if (c != EOF)
{
// insert char into 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;
}
};
#endif /* IO_OUTBUF3_HPP_ */
本文详细介绍了C++中的Stream缓冲区,包括如何使用streambuf进行读写操作,如sputc、sputn等函数,以及输入输出缓冲区迭代器的使用。此外,还展示了自定义Stream缓冲区的实现,包括通过重载overflow和xsputn来优化字符处理。文章通过实例演示了如何利用streambuf迭代器进行无格式I/O操作。
214

被折叠的 条评论
为什么被折叠?



