C++ Stream Classes 之 Stream Buffer Classes

本文详细介绍了C++中的Stream缓冲区,包括如何使用streambuf进行读写操作,如sputc、sputn等函数,以及输入输出缓冲区迭代器的使用。此外,还展示了自定义Stream缓冲区的实现,包括通过重载overflow和xsputn来优化字符处理。文章通过实例演示了如何利用streambuf迭代器进行无格式I/O操作。

东阳的学习笔记

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 缓冲区迭代器

  1. 符合input迭代器和 output 迭代器的规格
  2. 从 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缓冲区迭代器

  1. 输入流缓冲区迭代器和流istream迭代器所有操作类似
  2. 成员函数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()/ ==判断两个迭代器相等
!=判断两个迭代器不等

说明:

  1. 从流的当前位置到结尾: 由两个迭代器定义:istreambuf_iterator<char>(istream)表示开始,istreambuf_iterator<char>()表示结尾;
  2. 不可能以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()取得。他们的意义是:

  1. pbase() (put base):是 output stream 缓冲区的开始。
  2. pptr()(put pointer):是当前写入位置
  3. 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_ */

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东阳z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值