1. 程式人生 > >使用zlib庫的compress函式與uncompress函式

使用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壓縮一下;接收方收到資料後再解壓一下,這樣接收方就能得到原始資料了。

參考文章:

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