使用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函式
宣告如下:
int compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);/* 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. */
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、使用示例
// 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、函式使用說明
5、應用場景
(1)節約空間
當需要將一個很佔記憶體的變數寫入檔案以節省記憶體時,可以先壓縮一下,然後寫入檔案,當需要使用時再從檔案中讀出,然後解壓縮,以便節省IO時間。此外,有些情況可能還要序列化一下,當要壓縮的記憶體不是一段連續的記憶體的時候。
(2)節約網路流量
待發送的資料包比較大,且網路流量有限時,這兩個函式也能派上用場。傳送資料包前,先將資料包buffer壓縮一下;接收方收到資料後再解壓一下,這樣接收方就能得到原始資料了。
參考文章:
3、http://blog.csdn.net/zhongguoren666/article/details/7076258