C++?MiniZip實(shí)現(xiàn)目錄壓縮與解壓的示例詳解
Zlib是一個(gè)開源的數(shù)據(jù)壓縮庫,提供了一種通用的數(shù)據(jù)壓縮和解壓縮算法。它最初由Jean-Loup Gailly
和Mark Adler
開發(fā),旨在成為一個(gè)高效、輕量級的壓縮庫,其被廣泛應(yīng)用于許多領(lǐng)域,包括網(wǎng)絡(luò)通信、文件壓縮、數(shù)據(jù)庫系統(tǒng)等。其壓縮算法是基于DEFLATE
算法,這是一種無損數(shù)據(jù)壓縮算法,通常能夠提供相當(dāng)高的壓縮比。
在Zlib項(xiàng)目中的contrib
目錄下有一個(gè)minizip
子項(xiàng)目,minizip實(shí)際上不是zlib
庫的一部分,而是一個(gè)獨(dú)立的開源庫,用于處理ZIP壓縮文件格式。它提供了對ZIP文件的創(chuàng)建和解壓的簡單接口。minizip在很多情況下與zlib
一起使用,因?yàn)閆IP壓縮通常使用了DEFLATE
壓縮算法。通過對minizip
庫的二次封裝則可實(shí)現(xiàn)針對目錄的壓縮與解壓功能。
如果你想使用minizip
通常你需要下載并編譯它,然后將其鏈接到你的項(xiàng)目中。
編譯Zlib庫很簡單,解壓文件并進(jìn)入到\zlib-1.3\contrib\vstudio
目錄下,根據(jù)自己編譯器版本選擇不同的目錄,這里我選擇vc12
,進(jìn)入后打開zlibvc.sln
等待生成即可。
成功后可獲得兩個(gè)文件分別是zlibstat.lib
和zlibwapi.lib
如下圖;
接著配置引用目錄,這里需要多配置一個(gè)minizip
頭文件,該頭文件是zlib里面的一個(gè)子項(xiàng)目。
lib庫則需要包含zlibstat.lib
和zlibwapi.lib
這兩個(gè)文件,此處讀者可以自行放入到一個(gè)目錄下;
ZIP 遞歸壓縮目錄
如下所示代碼是一個(gè)使用zlib庫實(shí)現(xiàn)的簡單文件夾壓縮工具的C++程序。該程序提供了壓縮文件夾到 ZIP 文件的功能,支持遞歸地添加文件和子文件夾,利用了 Windows API 和 zlib 庫的函數(shù)。
#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 nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile) { // 目錄如果為空則直接返回 if (NULL == zfile || fileNameinZip.empty()) { return 0; } int nErr = 0; zip_fileinfo zinfo = { 0 }; tm_zip tmz = { 0 }; zinfo.tmz_date = tmz; zinfo.dosDate = 0; zinfo.internal_fa = 0; zinfo.external_fa = 0; char sznewfileName[MAX_PATH] = { 0 }; memset(sznewfileName, 0x00, sizeof(sznewfileName)); strcat_s(sznewfileName, fileNameinZip.c_str()); if (srcfile.empty()) { strcat_s(sznewfileName, "\\"); } nErr = zipOpenNewFileInZip(zfile, sznewfileName, &zinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); if (nErr != ZIP_OK) { return false; } if (!srcfile.empty()) { // 打開源文件 FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO); if (NULL == srcfp) { std::cout << "打開源文件失敗" << std::endl; return false; } // 讀入源文件寫入zip文件 int numBytes = 0; char* pBuf = new char[1024 * 100]; if (NULL == pBuf) { std::cout << "新建緩沖區(qū)失敗" << std::endl; return 0; } while (!feof(srcfp)) { memset(pBuf, 0x00, sizeof(pBuf)); numBytes = fread(pBuf, 1, sizeof(pBuf), srcfp); nErr = zipWriteInFileInZip(zfile, pBuf, numBytes); if (ferror(srcfp)) { break; } } delete[] pBuf; fclose(srcfp); } zipCloseFileInZip(zfile); return true; } bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName) { if (NULL == zfile || filepath.empty()) { return false; } bool bFile = false; std::string relativepath = ""; WIN32_FIND_DATAA findFileData; char szpath[MAX_PATH] = { 0 }; if (::PathIsDirectoryA(filepath.c_str())) { strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str()); int len = strlen(szpath) + strlen("\\*.*") + 1; strcat_s(szpath, len, "\\*.*"); } else { bFile = true; strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str()); } HANDLE hFile = ::FindFirstFileA(szpath, &findFileData); if (NULL == hFile) { return false; } do { if (parentdirName.empty()) relativepath = findFileData.cFileName; else // 生成zip文件中的相對路徑 relativepath = parentdirName + "\\" + findFileData.cFileName; // 如果是目錄 if (findFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) { // 去掉目錄中的.當(dāng)前目錄和..前一個(gè)目錄 if (strcmp(findFileData.cFileName, ".") != 0 && strcmp(findFileData.cFileName, "..") != 0) { nyAddfiletoZip(zfile, relativepath, ""); char szTemp[MAX_PATH] = { 0 }; strcpy_s(szTemp, filepath.c_str()); strcat_s(szTemp, "\\"); strcat_s(szTemp, findFileData.cFileName); nyCollectfileInDirtoZip(zfile, szTemp, relativepath); } continue; } char szTemp[MAX_PATH] = { 0 }; if (bFile) { //注意:處理單獨(dú)文件的壓縮 strcpy_s(szTemp, filepath.c_str()); } else { //注意:處理目錄文件的壓縮 strcpy_s(szTemp, filepath.c_str()); strcat_s(szTemp, "\\"); strcat_s(szTemp, findFileData.cFileName); } nyAddfiletoZip(zfile, relativepath, szTemp); } while (::FindNextFileA(hFile, &findFileData)); FindClose(hFile); return true; } /* * 函數(shù)功能 : 壓縮文件夾到目錄 * 備 注 : dirpathName 源文件/文件夾 * zipFileName 目的壓縮包 * parentdirName 壓縮包內(nèi)名字(文件夾名) */ bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName) { bool bRet = false; /* APPEND_STATUS_CREATE 創(chuàng)建追加 APPEND_STATUS_CREATEAFTER 創(chuàng)建后追加(覆蓋方式) APPEND_STATUS_ADDINZIP 直接追加 */ zipFile zFile = NULL; if (!::PathFileExistsA(zipfileName.c_str())) { zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATE); } else { zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_ADDINZIP); } if (NULL == zFile) { std::cout << "創(chuàng)建ZIP文件失敗" << std::endl; return bRet; } if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName)) { bRet = true; } zipClose(zFile, NULL); return bRet; }
主要功能
nyCreateZipfromDir函數(shù)
bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);
功能:壓縮文件夾到指定的 ZIP 文件。
參數(shù):
- dirpathName:源文件夾路徑。
- zipfileName:目標(biāo) ZIP 文件路徑。
- parentdirName:在 ZIP 文件內(nèi)的文件夾名(如果為空則不指定目錄)。
nyCollectfileInDirtoZip 函數(shù)
bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName);
功能:遞歸地收集文件夾中的文件,并將它們添加到已打開的 ZIP 文件中。
參數(shù):
- zfile:已打開的 ZIP 文件。
- filepath:文件夾路徑。
- parentdirName:在 ZIP 文件內(nèi)的相對文件夾名。
nyAddfiletoZip 函數(shù)
bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile);
功能:將指定文件添加到已打開的 ZIP 文件中。
參數(shù):
- zfile:已打開的 ZIP 文件。
- fileNameinZip:在 ZIP 文件內(nèi)的相對文件路徑。
- srcfile:源文件路徑。
程序流程
- 文件夾壓縮參數(shù)設(shè)置: 用戶提供源文件夾路徑、目標(biāo) ZIP 文件路徑,以及在 ZIP 文件內(nèi)的文件夾名。
- ZIP 文件打開: 根據(jù)目標(biāo) ZIP 文件是否存在,使用 zipOpen 函數(shù)打開 ZIP 文件。
- 文件夾遞歸添加: 使用 nyCollectfileInDirtoZip 函數(shù)遞歸地收集文件夾中的文件,并通過 nyAddfiletoZip 函數(shù)將它們添加到 ZIP 文件中。
- ZIP 文件關(guān)閉: 使用 zipClose 函數(shù)關(guān)閉 ZIP 文件。
示例用法
int main(int argc, char* argv[]) { std::string dirpath = "D:\\lyshark\\test"; // 源文件/文件夾 std::string zipfileName = "D:\\lyshark\\test.zip"; // 目的壓縮包 bool ref = nyCreateZipfromDir(dirpath, zipfileName, "lyshark"); // 包內(nèi)文件名<如果為空則壓縮時(shí)不指定目錄> std::cout << "[LyShark] 壓縮狀態(tài) " << ref << std::endl; system("pause"); return 0; }
上述調(diào)用代碼,參數(shù)1指定為需要壓縮的文件目錄,參數(shù)2指定為需要壓縮成目錄名,參數(shù)3為壓縮后該目錄的名字。
ZIP 遞歸解壓目錄
在這個(gè)C++程序中,實(shí)現(xiàn)了遞歸解壓縮ZIP文件的功能。程序提供了以下主要功能:
- replace_all 函數(shù): 用于替換字符串中的指定子串。
- CreatedMultipleDirectory 函數(shù): 用于創(chuàng)建多級目錄,確保解壓縮時(shí)的目錄結(jié)構(gòu)存在。
- UnzipFile 函數(shù): 用于遞歸解壓縮 ZIP 文件。該函數(shù)打開 ZIP 文件,獲取文件信息,然后逐個(gè)解析和處理 ZIP 文件中的文件或目錄。
#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") // 將字符串內(nèi)的old_value替換成new_value std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value) { while (true) { std::string::size_type pos(0); if ((pos = str.find(old_value)) != std::string::npos) str.replace(pos, old_value.length(), new_value); else break; } return str; } // 創(chuàng)建多級目錄 BOOL CreatedMultipleDirectory(const std::string& direct) { std::string Directoryname = direct; if (Directoryname[Directoryname.length() - 1] != '\\') { Directoryname.append(1, '\\'); } std::vector< std::string> vpath; std::string strtemp; BOOL bSuccess = FALSE; for (int i = 0; i < Directoryname.length(); i++) { if (Directoryname[i] != '\\') { strtemp.append(1, Directoryname[i]); } else { vpath.push_back(strtemp); strtemp.append(1, '\\'); } } std::vector< std::string>::iterator vIter = vpath.begin(); for (; vIter != vpath.end(); vIter++) { bSuccess = CreateDirectoryA(vIter->c_str(), NULL) ? TRUE : FALSE; } return bSuccess; } /* * 函數(shù)功能 : 遞歸解壓文件目錄 * 備 注 : strFilePath 壓縮包路徑 * strTempPath 解壓到 */ void UnzipFile(const std::string& strFilePath, const std::string& strTempPath) { int nReturnValue; string tempFilePath; string srcFilePath(strFilePath); string destFilePath; // 打開zip文件 unzFile unzfile = unzOpen(srcFilePath.c_str()); if (unzfile == NULL) { return; } // 獲取zip文件的信息 unz_global_info* pGlobalInfo = new unz_global_info; nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo); if (nReturnValue != UNZ_OK) { std::cout << "數(shù)據(jù)包: " << pGlobalInfo->number_entry << endl; return; } // 解析zip文件 unz_file_info* pFileInfo = new unz_file_info; char szZipFName[MAX_PATH] = { 0 }; char szExtraName[MAX_PATH] = { 0 }; char szCommName[MAX_PATH] = { 0 }; // 存放從zip中解析出來的內(nèi)部文件名 for (int i = 0; i < pGlobalInfo->number_entry; i++) { // 解析得到zip中的文件信息 nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH); if (nReturnValue != UNZ_OK) return; std::cout << "解壓文件名: " << szZipFName << endl; string strZipFName = szZipFName; // 如果是目錄則執(zhí)行創(chuàng)建遞歸目錄名 if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1)) { destFilePath = strTempPath + "http://" + szZipFName; CreateDirectoryA(destFilePath.c_str(), NULL); } // 如果是文件則解壓縮并創(chuàng)建 else { // 創(chuàng)建文件 保存完整路徑 string strFullFilePath; tempFilePath = strTempPath + "/" + szZipFName; strFullFilePath = tempFilePath; int nPos = tempFilePath.rfind("/"); int nPosRev = tempFilePath.rfind("\\"); if (nPosRev == string::npos && nPos == string::npos) continue; size_t nSplitPos = nPos > nPosRev ? nPos : nPosRev; destFilePath = tempFilePath.substr(0, nSplitPos + 1); if (!PathIsDirectoryA(destFilePath.c_str())) { // 將路徑格式統(tǒng)一 destFilePath = replace_all(destFilePath, "/", "\\"); // 創(chuàng)建多級目錄 int bRet = CreatedMultipleDirectory(destFilePath); } strFullFilePath = replace_all(strFullFilePath, "/", "\\"); HANDLE hFile = CreateFileA(strFullFilePath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL); if (hFile == INVALID_HANDLE_VALUE) { return; } // 打開文件 nReturnValue = unzOpenCurrentFile(unzfile); if (nReturnValue != UNZ_OK) { CloseHandle(hFile); return; } // 讀取文件 uLong BUFFER_SIZE = pFileInfo->uncompressed_size;; void* szReadBuffer = NULL; szReadBuffer = (char*)malloc(BUFFER_SIZE); if (NULL == szReadBuffer) { break; } while (TRUE) { memset(szReadBuffer, 0, BUFFER_SIZE); int nReadFileSize = 0; nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE); // 讀取文件失敗 if (nReadFileSize < 0) { unzCloseCurrentFile(unzfile); CloseHandle(hFile); return; } // 讀取文件完畢 else if (nReadFileSize == 0) { unzCloseCurrentFile(unzfile); CloseHandle(hFile); break; } // 寫入讀取的內(nèi)容 else { DWORD dWrite = 0; BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL); if (!bWriteSuccessed) { unzCloseCurrentFile(unzfile); CloseHandle(hFile); return; } } } free(szReadBuffer); } unzGoToNextFile(unzfile); } delete pFileInfo; delete pGlobalInfo; // 關(guān)閉 if (unzfile) { unzClose(unzfile); } }
主要功能
replace_all 函數(shù)
std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
功能:在字符串 str 中替換所有的 old_value 為 new_value。
參數(shù):
- str:待處理的字符串。
- old_value:要被替換的子串。
- new_value:替換后的新子串。
返回值:替換后的字符串。
CreatedMultipleDirectory 函數(shù)
BOOL CreatedMultipleDirectory(const std::string& direct)
功能:創(chuàng)建多級目錄,確保路徑存在。
參數(shù):
- direct:目錄路徑。
- 返回值:如果成功創(chuàng)建目錄返回 TRUE,否則返回 FALSE。
UnzipFile 函數(shù)
void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)
功能:遞歸解壓縮 ZIP 文件。
參數(shù):
- strFilePath:ZIP 文件路徑。
- strTempPath:解壓到的目標(biāo)路徑。
該函數(shù)打開 ZIP 文件,獲取文件信息,然后逐個(gè)解析和處理 ZIP 文件中的文件或目錄。在解析過程中,根據(jù)文件或目錄的屬性,創(chuàng)建相應(yīng)的目錄結(jié)構(gòu),然后將文件寫入目標(biāo)路徑。
示例用法
int main(int argc, char* argv[]) { std::string srcFilePath = "D:\\lyshark\\test.zip"; std::string tempdir = "D:\\lyshark\\test"; // 如果傳入目錄不存在則創(chuàng)建 if (!::PathFileExistsA(tempdir.c_str())) { CreatedMultipleDirectory(tempdir); } // 調(diào)用解壓函數(shù) UnzipFile(srcFilePath, tempdir); system("pause"); return 0; }
案例中,首先在解壓縮之前判斷傳入目錄是否存在,如果不存在則需要調(diào)用API創(chuàng)建目錄,如果存在則直接調(diào)用UnzipFIle
解壓縮函數(shù),實(shí)現(xiàn)解包,輸出效果圖如下;
到此這篇關(guān)于C++ MiniZip實(shí)現(xiàn)目錄壓縮與解壓的示例詳解的文章就介紹到這了,更多相關(guān)C++ MiniZip目錄壓縮與解壓內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MFC實(shí)現(xiàn)在文件尾追加數(shù)據(jù)的方法
這篇文章主要介紹了MFC實(shí)現(xiàn)在文件尾追加數(shù)據(jù)的方法,涉及MFC文件操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09C語言實(shí)現(xiàn)隨機(jī)抽獎(jiǎng)程序
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)隨機(jī)抽獎(jiǎng)程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09C++初階之list的模擬實(shí)現(xiàn)過程詳解
在C++中我們經(jīng)常使用STL,那個(gè)在那些我們常用的數(shù)據(jù)結(jié)構(gòu)vector,list的背后,又是如何實(shí)現(xiàn)的呢?這篇文章主要給大家介紹了關(guān)于C++初階之list的模擬實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2021-08-08C語言的動(dòng)態(tài)內(nèi)存分配及動(dòng)態(tài)內(nèi)存分配函數(shù)詳解
這篇文章主要為大家詳細(xì)介紹了C語言的動(dòng)態(tài)內(nèi)存分配及動(dòng)態(tài)內(nèi)存分配函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03C++使用expected實(shí)現(xiàn)優(yōu)雅的錯(cuò)誤處理
C++ 中提供了很多中方式進(jìn)行錯(cuò)誤處理。無論是通過拋異常還是通過錯(cuò)誤碼,標(biāo)準(zhǔn)庫都提供相應(yīng)的調(diào)用,今天本文為大家介紹的是使用expected進(jìn)行錯(cuò)誤處理,感興趣的可以了解一下2023-06-06