zipstream, bzip2stream:zlib 和 bzip2 库的 iostream 包装器






4.65/5 (41投票s)
2003年7月2日
5分钟阅读

367568

12071
符合 STL 标准,支持流到流的 zlib 和 bzip2 包装器,支持宽字符。
- 下载 zlib 包装器源文件 - 3.35 Kb
- 下载 bzip2 包装器源文件 - 4.42 Kb
- 下载演示项目(包含 zlib 和 bzip2 库) - 248 Kb
- 下载 HTML 文档文件 - 170 Kb
引言
本文介绍了两个基于 zlib(参见上面的下载链接)和 bzip2(参见上面的下载链接)库的压缩 STL iostream
实现。这意味着您可以轻松地像操作其他 STL ostream
/istream
一样操作压缩流。
为了让您有一个概念,请看下面打印“Hello World”的片段:
ostringstream output_buffer; // writing data output_buffer<<"Hello world"<<endl
现在,使用 zlib
进行压缩输出的相同片段:
// zip_ostream uses output_buffer as output buffer :) zip_ostream zipper( output_buffer ); // writing data as usual zipper<<"Hello world"<<endl
或者使用 bzip2
:
// zip_ostream uses output_buffer as output buffer :) bzip2_ostream bzipper( output_buffer ); // writing data as usual bzipper<<"Hello world"<<endl
正如您所见,将压缩缓冲区添加到现有应用程序非常简单。总而言之,让我们快速了解一下 zipstream
和 bzip2stream
的一些事实:
- 符合 STL 标准,
- 支持任意流到任意流,
- 支持
char
,wchar_t
, - 精细调整压缩属性,
- 支持自定义分配器(新增!)
为什么还要写一个包装器?为什么不使用 gzstream?
在 CodeProject 上,围绕 zlib
库编写包装器非常普遍。如果您搜索“zip”,您会找到至少 14 篇关于此主题的文章。此外,如果您浏览网络,尤其是在 zlib
主页上,您可以找到几十个其他包装器。
那么为什么还要写一个包装器呢?嗯,没有一个包装器是完全符合 STL 标准的。好吧,这并非完全属实,因为 gzstream
(参见上面的下载链接)实现了类似 fstream
的 STL 流。但是,gzstream
有三个缺点:
- 它不支持缓冲区到缓冲区的压缩,因为它基于
gzip
的 i/o 方法:只支持文件到缓冲区或缓冲区到文件; - 它是在 LGPL 许可下发布的,这使得在商业应用程序中使用它变得困难;
- 它不支持
wchar_t
。
撰写此包装器的最后一个原因:这是一个理解和实现 iostreams
的好练习。
包装器架构
gzstream
的三个缺点促使我重新实现了 zlib
的 STL 包装器(之后,进行了一些复制粘贴以使 bzip2
生效)。
此包装器接受用户定义的 i/ostream
来读取或写入压缩数据。这种方法非常灵活,因为用户可以提供任何流(istringstream
、ifstream
或自定义流)来存储或加载压缩数据。
内部 zip_stream
充当三层缓冲区:streambuf
对象本身、zlib
库以及用户定义的流。例如,在压缩过程中,缓冲区的使用方式如下:
- 第一层缓冲区:要压缩的数据被缓冲到一个
streambuf
对象中; - 第二层缓冲区:当调用
overflow
时,第一层缓冲区的数据被发送到zlib
,zlib
也会在其内部缓冲数据。如果zlib
输出数据,则会将其发送到用户定义的流; - 第三层缓冲区:用户定义的流被缓冲。
在刷新时必须小心:您必须使用 zflush
方法,该方法将首先刷新 streambuf
,然后刷新 zlib
缓冲区,最后刷新用户定义的流。请注意,您应该避免刷新,因为它会降低压缩效果。
实现 iostreams
由于我不是 STL 专家,我将非常简要地讨论这部分。这部分有空间写一篇教程...
要实现自定义 iostream
,您需要执行以下步骤:
- 实现一个自定义的
my_streambuf
,继承自streambuf
。您需要重写虚方法sync
、underflow
和overflow
。sync
和overflow
用于输出流,underflow
用于输入流。 - 实现一个自定义的
my_ostream
,继承自ostream
。它将使用my_streambuf
。 - 实现一个自定义的
my_istream
,继承自istream
。它将使用my_streambuf
作为流缓冲区。
类快速参考
所有 zlib
类都在 zlib_stream
命名空间中,所有 bzip2
类都在 bzip2_stream
命名空间中。
zlib
包装器的两个主要类是 basic_zip_ostream
和 basic_zip_istream
,它们分别实现压缩和解压缩,并且行为类似于经典的 basic_ostream
和 basic_istream
。
还提供了这些类的经典 typedef
:
zip_ostream
,zip_istream
用于char
流;wzip_ostream
,wzip_istream
用于wchar_t
流。
bzip2 类具有相似的名称,只需将 zlib 替换为 bzip2:basic_zip_streambuf
变为 basic_bzip2_streambuf
。
basic_zip_ostream
此类继承自 basic_ostream
。
template<
typename Elem,
typename Tr = char_traits<Elem;>,
typename ElemA = std::allocator<Elem>,
typename ByteT = unsigned char,
typename ByteAllocatorT = std::allocator<ByteT>
>
basic_zip_ostream : public basic_ostream<Elem, Tr>
其中
Elem,Tr
是经典的basic_ostream
模板参数;ElemA
是内部使用的Elem
缓冲区的分配器;ByteT
是内部使用的字节类型(您不应更改此项);ByteAT
是内部使用的ByteT
缓冲区的自定义分配器。
构造函数
basic_zip_ostream(
ostream_reference ostream_,
bool is_gzip_ = false,
size_t level_ = Z_DEFAULT_COMPRESSION,
EStrategy strategy_ = DefaultStrategy,
size_t window_size_ = 15,
size_t memory_level_ = 8,
size_t buffer_size_ = 4096
);
ostream_
是用户定义的输出流;is_gzip_
,如果希望添加gzip
头和尾,则为 true;level_
,压缩级别 0,较差但速度快,到 9,最大但较慢;strategy_
,压缩策略,请参阅EStrategy
枚举;window_size_
,memory_level_
是高级zlib
设置,请查阅zlib
手册;buffer_size_
,读取缓冲区大小。
请注意,如果您选择 gzip
选项,则会在构造函数中自动添加头,并在析构函数中添加 gzip
尾(CRC + 数据大小)。
其他方法
- 刷新所有缓冲区(
zlib
和ostream
)。basic_zip_ostream& zflush()
在使用压缩数据之前必须调用此方法!由于
zlib
会进行自己的缓冲,并且ostream::flush
不是虚函数,因此无法避免此问题。 - 返回未压缩数据的 CRC。
long get_crc();
- 返回未压缩数据的大小。
long get_in_size();
- 返回压缩数据的大小。
long get_out_size();
预定义的 typedef。
typedef basic_zip_ostream<char> zip_ostream;
typedef basic_zip_ostream<wchar_t> zip_wostream;
basic_zip_istream
此类继承自 basic_istream
。
template<
typename Elem,
typename Tr = char_traits<Elem;>,
typename ElemA = std::allocator<Elem>,
typename ByteT = unsigned char,
typename ByteAT = std::allocator<ByteT>
>
basic_zip_istream : public basic_istream<Elem, Tr>
构造函数
basic_zip_istream(
istream_reference istream_,
size_t window_size_ = 15,
size_t read_buffer_size_ = 1024 * 10,
size_t input_buffer_size_ = 1024 * 5
)
istream_
,包含压缩数据的输入流;window_size_
,应与压缩窗口大小兼容;read_buffer_size_
,streambuf
缓冲区的大小;input_buffer_size_
,zlib
输入缓冲区的大小。
其他方法
- 指示是否为
gzip
文件。bool is_gzip() const
- 检查 CRC(必须是
gzip
文件)。bool check_crc() const
- 返回未压缩数据的 CRC。
long get_crc() const;
- 返回未压缩数据的大小。
long get_out_size() const;
- 返回压缩数据的大小。
long get_in_size() const;
预定义的 typedef。
typedef basic_zip_istream<char> zip_istream; typedef basic_zip_istream<wchar_t> zip_wistream;
如何...
以下所有示例对 zlib 和 bzip2 包装器都有效。
压缩到缓冲区。
ostringstream buffer;
zip_ostream zipper(buffer);
// writing stuff
zipper<<...
//flushing VERY IMPORTANT!
zipper.zflush();
// buffer.str() is ready
压缩到文件。
ofstream file("test.zip",ios::out | ios::binary);
{
zip_ostream zipper(file, true /* gzip file*/);
// writing stuff
zipper<<...
} // the stream is flushed, the destructor is called and gzip header appended
// the file is ready
从缓冲区解压缩。
istringstream buffer;
zip_istream unzipper(buffer);
// reading stuff
unzipper>>...
从文件解压缩。
ifstream file("test.zip", ios::in | ios::binary);
zip_istream unzipper(file);
// reading stuff
unzipper>>...
// if the file was gzip, we can check the crc
if (unzipper.is_gzip())
std::cout<<"crc check: "<<( unzipper.check_crc() ? "ok" : "failed");
在您的项目中进行使用
Zlib 包装器
- 阅读许可条款;
- 将 zipstream.hpp 和 zipstream.ipp 复制到您的包含目录;
- 确保
zlib
可用; - 添加
#include "zlibstream.hpp"
来包含头文件;
bzip2 包装器
- 阅读许可条款;
- 将 bzip2stream.hpp 和 bzip2stream.ipp 复制到您的包含目录;
- 确保
zlib
可用; - 添加
#include "bzip2stream.hpp"
来包含头文件;
历史
- 2003-09-30, 1.7, 添加了自定义分配器(由 <forgot, send me your e-mail> 提出建议),修复了
ostream
构造函数中的警告。 - 2003-09-21, 修复了 CRC 和大小的错误,感谢 Jeroen Dirks 和 gigimenegolo 的贡献。
- 08-08-2003, 1.5
- 修复了 gzip 尾部问题:读取 CRC。
- 当 zip 文件结束时,数据被放回缓冲区。
- 2003-07-18, 1.4 修复了 gzip 头问题。
- 2003-07-03, 1.3, 添加了 bzip2 包装器。
- 2003-07-02, 1.2,
wchar_t
可用。 - 2003-07-02, 1.1, 修复了 gzip 头和
zip_to_stream
中的错误。 - 2003-07-01, 1.0, 初始发布。
参考
许可证
这些 zlib
和 bzip2
包装器在 zlib/libpng 许可证下发布。