使用zlib库的compress函数与uncompress函数

        zlib是一个免费、通用、无损的数据压缩库,而且还是跨平台的。zlib具有同winzip和winrar等商业软件相比毫不逊色的压缩率,已经成功应用在诸如MySQL、Java、3DMax、甚至是微软的DirectX等大型的系统中。目前Z1ib的最新版本是1.2.8。  

        zlib的最新版本可以在http://www.zlib.net下载,下载后只需用Visual C++编译该工程即可得到开发所需的zlib.h、zconf.h、 zlib.lib和zlib.dll等文件。  

        本文主要研究zlib的内存数据压缩方法。zlib提供了一套 in-memory 压缩和解压函数,并能检测解压出来的数据的完整性(integrity)。zlib 也支持读写 gzip (.gz) 格式的文件(注意:zlib函式库本身不能创建一个gzip文件,但是它相当轻松的通过把压缩数据写入到一个有gzip文件头的文件中)。下面介绍两个最有用的函数:compress 和 uncompress。

      1、compress函数

       声明如下:

/*
     Compresses the source buffer into the destination buffer.  sourceLen is
   the byte length of the source buffer. Upon entry, destLen is the total
   size of the destination buffer, which must be at least the value returned
   by compressBound(sourceLen). Upon exit, destLen is the actual size of the
   compressed buffer.
     This function can be used to compress a whole file at once if the
   input file is mmap'ed.
     compress returns Z_OK if success, Z_MEM_ERROR if there was not
   enough memory, Z_BUF_ERROR if there was not enough room in the output
   buffer.
*/
int compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
         compress函数将 source 缓冲区中的内容压缩到 dest 缓冲区。 sourceLen 表示source 缓冲区的大小(以字节计)。注意函数的第二个参数 destLen 是传址调用。当调用函数时,destLen表示 dest 缓冲区的大小,zlib本身有提供compressBound函数用于计算压缩后缓冲区长度的上限值,不需要额外再设计一些不适当的预测算法(注意2002年的版本没有compressBound函数,2004年及以后的版本都是有的)。当函数退出后,destLen 表示压缩后缓冲区的实际大小。此时 destLen / sourceLen 正好是压缩率。

        若compress 若成功,则返回 Z_OK;若没有足够内存,则返回 Z_MEM_ERROR;若输出缓冲区不够大,则返回 Z_BUF_ERROR。

     2、uncompress函数

       声明如下:

/*
     Decompresses the source buffer into the destination buffer.  sourceLen is
   the byte length of the source buffer. Upon entry, destLen is the total
   size of the destination buffer, which must be large enough to hold the
   entire uncompressed data. (The size of the uncompressed data must have
   been saved previously by the compressor and transmitted to the decompressor
   by some mechanism outside the scope of this compression library.)
   Upon exit, destLen is the actual size of the compressed buffer.
     This function can be used to decompress a whole file at once if the
   input file is mmap'ed.

     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
   enough memory, Z_BUF_ERROR if there was not enough room in the output
   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
*/
int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
        uncompress 函数将 source 缓冲区的内容解压缩到 dest 缓冲区。sourceLen 是 source 缓冲区的大小(以字节计)。注意函数的第二个参数 destLen 是传址调用。当调用函数时,destLen 表示 dest 缓冲区的大小, dest 缓冲区要足以容下解压后的数据。在进行解压缩时,需要提前知道被压缩的数据解压出来会有多大。这就要求在进行压缩之前,保存原始数据的大小(也就是解压后的数据的大小)。这不是 zlib 函数库的功能,需要我们做额外的工作。当函数退出后, destLen 是解压出来的数据的实际大小。
        若uncompress 若成功,则返回 Z_OK ;若没有足够内存,则返回 Z_MEM_ERROR;若输出缓冲区不够大,则返回 Z_BUF_ERROR。若输入数据有误,则返回 Z_DATA_ERROR。

     3、使用示例

       zlib 自带的 example.c 是个很好的使用示例。我们写个程序,验证 zlib 的压缩功能。所写的测试程序保存为 testzlib.cpp ,放在 zlib-1.1.4 目录下。程序源代码:

// testzlib.cpp  简单测试 zlib 的压缩功能
#include <cstring>
#include <cstdlib>
#include <iostream>
#include "zlib.h"

using namespace std;

int main()
{
    int err;
    Byte compr[200], uncompr[200];    // big enough
    uLong comprLen, uncomprLen;

    const char* hello = "12345678901234567890123456789012345678901234567890";
    uLong len = strlen(hello) + 1;
    comprLen  = sizeof(compr) / sizeof(compr[0]);

    err = compress(compr, &comprLen, (const Bytef*)hello, len);
    if (err != Z_OK) 
    {
        cerr << "compess error: " << err << '\n';
        exit(1);
    }

    cout << "orignal size: " << len
         << " , compressed size : " << comprLen << '\n';

    strcpy((char*)uncompr, "garbage");

    err = uncompress(uncompr, &uncomprLen, compr, comprLen);
    if (err != Z_OK) 
    {
        cerr << "uncompess error: " << err << '\n';
        exit(1);
    }

    cout << "orignal size: " << len
         << " , uncompressed size : " << uncomprLen << '\n';

    if (strcmp((char*)uncompr, hello)) 
    {
        cerr << "BAD uncompress!!!\n";
        exit(1);
    } else 
    {
        cout << "uncompress() succeed: \n" << (char *)uncompr;
    }
}
        编译执行这个程序,输出结果如下:

D:\libpng\zlib-1.1.4>bcc32 testzlib.cpp zlib.lib
D:\libpng\zlib-1.1.4>testzlib
orignal size: 51 , compressed size : 22
orignal size: 51 , uncompressed size : 51
uncompress() succeed:
12345678901234567890123456789012345678901234567890  

   4、函数使用说明

        zlib处理的对象是Bytef*字节流,很多人遇到字符串就会混淆了,其实很简单,字节流是没有结束符的,需要配备长度信息,所以处理字符串的时候需要把结束符也当成一个普通的字节,这样计算长度的时候也需要算它一份。另外绝大部分人都想动态分配缓冲区,也就是说需要多少再给多少,其实zlib本身有提供compressBound函数用于计算压缩后缓冲区长度的上限值,不需要额外再设计一些不适当的预测算法,不过解压缩的时候没有提供长度的预测。一般是预估目标buffer的长度,但可能存在预估不准确的情况,因为针对不同的压缩内容,压缩比是不一样的。如果是网络通信发过来的,在发送压缩过后的buffer时,也将压缩前的长度也穿过来。

   5、应用场景

         (1)节约空间

        当需要将一个很占内存的变量写入文件以节省内存时,可以先压缩一下,然后写入文件,当需要使用时再从文件中读出,然后解压缩,以便节省IO时间。此外,有些情况可能还要序列化一下,当要压缩的内存不是一段连续的内存的时候。


       (2)节约网络流量    

        待发送的数据包比较大,且网络流量有限时,这两个函数也能派上用场。发送数据包前,先将数据包buffer压缩一下;接收方收到数据后再解压一下,这样接收方就能得到原始数据了。


参考文章:

1、http://blog.csdn.net/turingo/article/details/8148264

2、http://www.cnblogs.com/fairycao/archive/2009/12/09/1620414.html

3、 http://blog.csdn.net/zhongguoren666/article/details/7076258

你可能感兴趣的:(使用zlib库的compress函数与uncompress函数)