65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (41投票s)

2003年7月2日

5分钟阅读

viewsIcon

367568

downloadIcon

12071

符合 STL 标准,支持流到流的 zlib 和 bzip2 包装器,支持宽字符。

引言

本文介绍了两个基于 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 

正如您所见,将压缩缓冲区添加到现有应用程序非常简单。总而言之,让我们快速了解一下 zipstreambzip2stream 的一些事实:

  • 符合 STL 标准,
  • 支持任意流到任意流,
  • 支持 char, wchar_t
  • 精细调整压缩属性,
  • 支持自定义分配器(新增!)

为什么还要写一个包装器?为什么不使用 gzstream?

在 CodeProject 上,围绕 zlib 库编写包装器非常普遍。如果您搜索“zip”,您会找到至少 14 篇关于此主题的文章。此外,如果您浏览网络,尤其是在 zlib 主页上,您可以找到几十个其他包装器。

那么为什么还要写一个包装器呢?嗯,没有一个包装器是完全符合 STL 标准的。好吧,这并非完全属实,因为 gzstream(参见上面的下载链接)实现了类似 fstream 的 STL 流。但是,gzstream 有三个缺点:

  1. 它不支持缓冲区到缓冲区的压缩,因为它基于 gzip 的 i/o 方法:只支持文件到缓冲区或缓冲区到文件;
  2. 它是在 LGPL 许可下发布的,这使得在商业应用程序中使用它变得困难;
  3. 它不支持 wchar_t

撰写此包装器的最后一个原因:这是一个理解和实现 iostreams 的好练习。

包装器架构

gzstream 的三个缺点促使我重新实现了 zlib 的 STL 包装器(之后,进行了一些复制粘贴以使 bzip2 生效)。

此包装器接受用户定义的 i/ostream 来读取或写入压缩数据。这种方法非常灵活,因为用户可以提供任何流(istringstreamifstream 或自定义流)来存储或加载压缩数据。

内部 zip_stream 充当三层缓冲区:streambuf 对象本身、zlib 库以及用户定义的流。例如,在压缩过程中,缓冲区的使用方式如下:

  • 第一层缓冲区:要压缩的数据被缓冲到一个 streambuf 对象中;
  • 第二层缓冲区:当调用 overflow 时,第一层缓冲区的数据被发送到 zlibzlib 也会在其内部缓冲数据。如果 zlib 输出数据,则会将其发送到用户定义的流;
  • 第三层缓冲区:用户定义的流被缓冲。

在刷新时必须小心:您必须使用 zflush 方法,该方法将首先刷新 streambuf,然后刷新 zlib 缓冲区,最后刷新用户定义的流。请注意,您应该避免刷新,因为它会降低压缩效果。

实现 iostreams

由于我不是 STL 专家,我将非常简要地讨论这部分。这部分有空间写一篇教程...

要实现自定义 iostream,您需要执行以下步骤:

  • 实现一个自定义的 my_streambuf,继承自 streambuf。您需要重写虚方法 syncunderflowoverflowsyncoverflow 用于输出流,underflow 用于输入流。
  • 实现一个自定义的 my_ostream,继承自 ostream。它将使用 my_streambuf
  • 实现一个自定义的 my_istream,继承自 istream。它将使用 my_streambuf 作为流缓冲区。

类快速参考

所有 zlib 类都在 zlib_stream 命名空间中,所有 bzip2 类都在 bzip2_stream 命名空间中。

zlib 包装器的两个主要类是 basic_zip_ostreambasic_zip_istream,它们分别实现压缩和解压缩,并且行为类似于经典的 basic_ostreambasic_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 + 数据大小)。

其他方法

  • 刷新所有缓冲区(zlibostream)。
    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.hppzipstream.ipp 复制到您的包含目录;
  • 确保 zlib 可用;
  • 添加 #include "zlibstream.hpp" 来包含头文件;

bzip2 包装器

  • 阅读许可条款;
  • bzip2stream.hppbzip2stream.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, 初始发布。

参考

许可证

这些 zlibbzip2 包装器在 zlib/libpng 许可证下发布。

© . All rights reserved.