C/C++使用Zlib實現(xiàn)文件的壓縮與解壓
在軟件開發(fā)和數(shù)據(jù)處理中,對數(shù)據(jù)進(jìn)行高效的壓縮和解壓縮是一項重要的任務(wù)。這不僅有助于減小數(shù)據(jù)在網(wǎng)絡(luò)傳輸和存儲中的占用空間,還能提高系統(tǒng)的性能和響應(yīng)速度。本文將介紹如何使用 zlib 庫進(jìn)行數(shù)據(jù)的壓縮和解壓縮,以及如何保存和讀取壓縮后的文件。zlib 是一個開源的數(shù)據(jù)壓縮庫,旨在提供高效、輕量級的壓縮和解壓縮算法。其核心壓縮算法基于 DEFLATE,這是一種無損數(shù)據(jù)壓縮算法,通常能夠提供相當(dāng)高的壓縮比。zlib 庫廣泛應(yīng)用于多個領(lǐng)域,包括網(wǎng)絡(luò)通信、文件壓縮、數(shù)據(jù)庫系統(tǒng)等。
保存文件
使用 CreateFile 打開文件,通過 WriteFile 向文件中寫出數(shù)據(jù),最后調(diào)用 CloseHandle 關(guān)閉句柄,實現(xiàn)文件的保存。
#define ZLIB_WINAPI
#include <string>
#include <iostream>
#include <vector>
#include <Shlwapi.h>
#include <zip.h>
#include <unzip.h>
#include <zlib.h>
using namespace std;
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "zlibstat.lib")
BOOL SaveToFile(char *pszFileName, BYTE *pData, DWORD dwDataSize)
{
char szSaveName[MAX_PATH] = { 0 };
lstrcpy(szSaveName, pszFileName);
HANDLE hFile = CreateFile(szSaveName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_ARCHIVE, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
return FALSE;
}
DWORD dwRet = 0;
WriteFile(hFile, pData, dwDataSize, &dwRet, NULL);
CloseHandle(hFile);
return TRUE;
}
int main(int argc, char * argv[])
{
char szBuffer[1024] = { 0 };
strcpy(szBuffer, "test 123123");
SaveToFile("d://test.txt", (BYTE *)szBuffer, sizeof(szBuffer));
system("pause");
return 0;
}
文件壓縮
compress 是 zlib 庫提供的用于數(shù)據(jù)壓縮的函數(shù),通過該函數(shù)可以將數(shù)據(jù)進(jìn)行壓縮。下面是一個示例,演示了如何使用 zlib 庫進(jìn)行文件壓縮。
它的原型如下:
int compress(Bytef* dest, uLongf* destLen, const Bytef* source, uLong sourceLen);
dest:指向存放壓縮后數(shù)據(jù)的緩沖區(qū)的指針。destLen:傳入時為壓縮緩沖區(qū)的大小,傳出時為實際壓縮后數(shù)據(jù)的大小。source:指向待壓縮數(shù)據(jù)的緩沖區(qū)的指針。sourceLen:待壓縮數(shù)據(jù)的大小。
compress 函數(shù)的作用是將 source 指向的數(shù)據(jù)進(jìn)行壓縮,并將結(jié)果存放在 dest 指向的緩沖區(qū)中。destLen 傳入時應(yīng)該是 dest 緩沖區(qū)的大小,函數(shù)執(zhí)行后,destLen 會更新為實際壓縮后數(shù)據(jù)的大小。
函數(shù)返回值為壓縮的執(zhí)行狀態(tài),可能的返回值包括:
Z_OK:壓縮成功。Z_MEM_ERROR:內(nèi)存分配失敗。Z_BUF_ERROR:壓縮輸出緩沖區(qū)不足。
這個函數(shù)實際上是使用 DEFLATE 算法進(jìn)行壓縮,DEFLATE 是一種通用的壓縮算法,也是 zlib 庫的核心算法之一。壓縮后的數(shù)據(jù)可以使用 uncompress 函數(shù)進(jìn)行解壓縮。
總體而言,compress 函數(shù)提供了一種簡單的方式,可以在應(yīng)用程序中對數(shù)據(jù)進(jìn)行壓縮,適用于需要減小數(shù)據(jù)體積的場景,比如網(wǎng)絡(luò)傳輸或數(shù)據(jù)存儲。
// 單個文件限制大小為 100M
#define MAX_SRC_FILE_SIZE (100*1024*1024)
/**
* @brief 壓縮指定文件的數(shù)據(jù)
*
* @param pszCompressFileName 待壓縮文件的路徑
* @param ppCompressData 保存壓縮后數(shù)據(jù)的指針
* @param pdwCompressDataSize 傳入時為壓縮緩沖區(qū)的大小,傳出時為實際壓縮后數(shù)據(jù)的大小
* @return 壓縮是否成功,成功返回 TRUE,否則返回 FALSE
*/
BOOL CompressData(char *pszCompressFileName, BYTE **ppCompressData, DWORD *pdwCompressDataSize)
{
HANDLE hFile = CreateFile(pszCompressFileName, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_ARCHIVE, NULL);
// 檢查文件句柄是否有效
if (INVALID_HANDLE_VALUE == hFile)
{
return FALSE;
}
// 獲取文件大小
DWORD dwFileSize = GetFileSize(hFile, NULL);
// 檢查文件大小是否超過限制
if (MAX_SRC_FILE_SIZE < dwFileSize)
{
CloseHandle(hFile);
return FALSE;
}
DWORD dwDestDataSize = dwFileSize;
// 分配源數(shù)據(jù)和目標(biāo)數(shù)據(jù)的內(nèi)存
BYTE *pSrcData = new BYTE[dwFileSize];
if (NULL == pSrcData)
{
CloseHandle(hFile);
return FALSE;
}
BYTE *pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData)
{
delete[] pSrcData;
CloseHandle(hFile);
return FALSE;
}
DWORD dwRet = 0;
// 讀取源數(shù)據(jù)
ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL);
// 檢查讀取是否成功
if ((0 >= dwRet) || (dwRet != dwFileSize))
{
delete[] pDestData;
delete[] pSrcData;
CloseHandle(hFile);
return FALSE;
}
int iRet = 0;
// 壓縮數(shù)據(jù)
do
{
iRet = compress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
// 壓縮成功,退出循環(huán)
if (0 == iRet)
{
break;
}
// 輸出緩沖區(qū)不足,增加緩沖區(qū)大小并重試
else if (-5 == iRet)
{
delete[] pDestData;
pDestData = NULL;
dwDestDataSize = dwDestDataSize + (100 * 1024);
pDestData = new BYTE[dwDestDataSize];
// 分配新的目標(biāo)數(shù)據(jù)內(nèi)存
if (NULL == pDestData)
{
delete[] pSrcData;
CloseHandle(hFile);
return FALSE;
}
}
// 壓縮失敗,釋放內(nèi)存并返回失敗
else
{
delete[] pDestData;
pDestData = NULL;
delete[] pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}
} while (TRUE);
// 保存壓縮后數(shù)據(jù)的指針和實際大小
*ppCompressData = pDestData;
*pdwCompressDataSize = dwDestDataSize;
// 釋放源數(shù)據(jù)內(nèi)存
delete[] pSrcData;
// 關(guān)閉文件句柄
CloseHandle(hFile);
// 返回壓縮成功
return TRUE;
}文件解壓縮
uncompress 函數(shù)是 zlib 庫提供的用于數(shù)據(jù)解壓縮的函數(shù),通過該函數(shù)可以將壓縮后的數(shù)據(jù)解壓縮還原。下面是一個示例,演示了如何使用 zlib 庫進(jìn)行文件解壓縮。
它的原型如下:
int uncompress(Bytef* dest, uLongf* destLen, const Bytef* source, uLong sourceLen);
dest:指向存放解壓縮后數(shù)據(jù)的緩沖區(qū)的指針。destLen:傳入時為解壓縮緩沖區(qū)的大小,傳出時為實際解壓縮后數(shù)據(jù)的大小。source:指向待解壓縮數(shù)據(jù)的緩沖區(qū)的指針。sourceLen:待解壓縮數(shù)據(jù)的大小。
uncompress 函數(shù)的作用是將 source 指向的數(shù)據(jù)進(jìn)行解壓縮,并將結(jié)果存放在 dest 指向的緩沖區(qū)中。destLen 傳入時應(yīng)該是 dest 緩沖區(qū)的大小,函數(shù)執(zhí)行后,destLen 會更新為實際解壓縮后數(shù)據(jù)的大小。
函數(shù)返回值為解壓縮的執(zhí)行狀態(tài),可能的返回值包括:
Z_OK:解壓縮成功。Z_MEM_ERROR:內(nèi)存分配失敗。Z_BUF_ERROR:解壓縮輸出緩沖區(qū)不足。Z_DATA_ERROR:輸入數(shù)據(jù)錯誤或損壞。
uncompress 函數(shù)實際上是使用 DEFLATE 算法進(jìn)行解壓縮,與 compress 函數(shù)相對應(yīng)。這兩個函數(shù)共同構(gòu)成了 zlib 庫中的基本數(shù)據(jù)壓縮和解壓縮功能。
在實際應(yīng)用中,可以使用這兩個函數(shù)來處理需要壓縮和解壓縮的數(shù)據(jù),例如在網(wǎng)絡(luò)通信中減小數(shù)據(jù)傳輸量或在存儲數(shù)據(jù)時減小占用空間。
/**
* @brief 解壓指定文件的數(shù)據(jù)
*
* @param pszUncompressFileName 待解壓文件的路徑
* @param ppUncompressData 保存解壓后數(shù)據(jù)的指針
* @param pdwUncompressDataSize 傳入時為解壓緩沖區(qū)的大小,傳出時為實際解壓后數(shù)據(jù)的大小
* @return 解壓是否成功,成功返回 TRUE,否則返回 FALSE
*/
BOOL UncompressData(char *pszUncompressFileName, BYTE **ppUncompressData, DWORD *pdwUncompressDataSize)
{
HANDLE hFile = CreateFile(pszUncompressFileName, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_ARCHIVE, NULL);
// 檢查文件句柄是否有效
if (INVALID_HANDLE_VALUE == hFile)
{
return FALSE;
}
// 獲取文件大小
DWORD dwFileSize = GetFileSize(hFile, NULL);
// 設(shè)置目標(biāo)數(shù)據(jù)緩沖區(qū)大小
DWORD dwDestDataSize = MAX_SRC_FILE_SIZE;
// 分配源數(shù)據(jù)和目標(biāo)數(shù)據(jù)的內(nèi)存
BYTE *pSrcData = new BYTE[dwFileSize];
if (NULL == pSrcData)
{
CloseHandle(hFile);
return FALSE;
}
BYTE *pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData)
{
delete[] pSrcData;
CloseHandle(hFile);
return FALSE;
}
DWORD dwRet = 0;
// 讀取源數(shù)據(jù)
ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL);
// 檢查讀取是否成功
if ((0 >= dwRet) || (dwRet != dwFileSize))
{
delete[] pDestData;
delete[] pSrcData;
CloseHandle(hFile);
return FALSE;
}
int iRet = 0;
// 解壓縮數(shù)據(jù)
do
{
iRet = uncompress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
// 解壓縮成功,退出循環(huán)
if (0 == iRet)
{
break;
}
// 輸出緩沖區(qū)不足,增加緩沖區(qū)大小并重試
else if (-5 == iRet)
{
delete[] pDestData;
pDestData = NULL;
dwDestDataSize = dwDestDataSize + (100 * 1024);
pDestData = new BYTE[dwDestDataSize];
// 分配新的目標(biāo)數(shù)據(jù)內(nèi)存
if (NULL == pDestData)
{
delete[] pSrcData;
CloseHandle(hFile);
return FALSE;
}
}
// 解壓縮失敗,釋放內(nèi)存并返回失敗
else
{
delete[] pDestData;
pDestData = NULL;
delete[] pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}
} while (TRUE);
// 保存解壓后數(shù)據(jù)的指針和實際大小
*ppUncompressData = pDestData;
*pdwUncompressDataSize = dwDestDataSize;
// 釋放源數(shù)據(jù)內(nèi)存
delete[] pSrcData;
// 關(guān)閉文件句柄
CloseHandle(hFile);
// 返回解壓成功
return TRUE;
}
演示示例
下面是一個包含文件壓縮和解壓縮的完整示例,展示了如何將文件進(jìn)行壓縮保存,然后解壓還原。
調(diào)用CompressData壓縮文件,返回結(jié)果pCompressData存放文件內(nèi)存字節(jié),結(jié)果dwCompressDataSize存放長度,并調(diào)用SaveToFile保存到本地。
int main(int argc, char* argv[])
{
BOOL bRet = FALSE;
BYTE *pCompressData = NULL;
DWORD dwCompressDataSize = 0;
// 壓縮文件
bRet = CompressData("d:\\test.exe", &pCompressData, &dwCompressDataSize);
if (TRUE == bRet)
{
std::cout << "已壓縮" << std::endl;
}
// 保存壓縮數(shù)據(jù)為文件
bRet = SaveToFile("d:\\text.zlib", pCompressData, dwCompressDataSize);
if (TRUE == bRet)
{
std::cout << "已保存到文件" << std::endl;
}
// 釋放內(nèi)存
delete[]pCompressData;
pCompressData = NULL;
system("pause");
return 0;
}
調(diào)用UncompressData解壓縮文件,返回結(jié)果pUncompressData存放文件內(nèi)存字節(jié),結(jié)果dwUncompressDataSize存放長度,并調(diào)用SaveToFile保存到本地。
int main(int argc, char* argv[])
{
BOOL bRet = FALSE;
BYTE *pUncompressData = NULL;
DWORD dwUncompressDataSize = 0;
// 解壓文件
bRet = UncompressData("d:\\test.zlib", &pUncompressData, &dwUncompressDataSize);
if (TRUE == bRet)
{
std::cout << "已解壓" << std::endl;
}
// 保存解壓數(shù)據(jù)為文件
bRet = SaveToFile("d:\\test.exe", pUncompressData, dwUncompressDataSize);
if (TRUE == bRet)
{
std::cout << "已保存到文件" << std::endl;
}
// 釋放內(nèi)存
delete[]pUncompressData;
pUncompressData = NULL;
system("pause");
return 0;
}
編譯時可能會提示無法生成SAFESEH影響的報錯信息,如下圖所示;

此時打開項目屬性頁,找到鏈接器,高級選項卡,將映像安全處理改為否即可,如下圖所示;

結(jié)論
通過使用 zlib 庫,我們可以方便地在應(yīng)用程序中實現(xiàn)數(shù)據(jù)的壓縮和解壓縮功能。這對于需要減小數(shù)據(jù)傳輸量或在存儲數(shù)據(jù)時減小占用空間的場景非常有用。在實際應(yīng)用中,可以根據(jù)需要調(diào)整緩沖區(qū)大小和處理流程,以適應(yīng)不同的數(shù)據(jù)處理需求。
以上就是C/C++使用Zlib實現(xiàn)文件的壓縮與解壓的詳細(xì)內(nèi)容,更多關(guān)于C++ Zlib文件壓縮與解壓的資料請關(guān)注腳本之家其它相關(guān)文章!

