C++ MiniZip实现目录压缩与解压

Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup GaillyMark Adler开发,旨在成为一个高效、轻量级的压缩库,其被广泛应用于许多领域,包括网络通信、文件压缩、数据库系统等。其压缩算法是基于DEFLATE算法,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。

在Zlib项目中的contrib目录下有一个minizip子项目,minizip实际上不是zlib库的一部分,而是一个独立的开源库,用于处理ZIP压缩文件格式。它提供了对ZIP文件的创建和解压的简单接口。minizip在很多情况下与zlib一起使用,因为ZIP压缩通常使用了DEFLATE压缩算法。通过对minizip库的二次封装则可实现针对目录的压缩与解压功能。

如果你想使用minizip通常你需要下载并编译它,然后将其链接到你的项目中。

  • Zlib源码:https://download.csdn.net/download/lyshark_csdn/88561117

编译Zlib库很简单,解压文件并进入到\zlib-1.3\contrib\vstudio目录下,根据自己编译器版本选择不同的目录,这里我选择vc12,进入后打开zlibvc.sln等待生成即可。

成功后可获得两个文件分别是zlibstat.libzlibwapi.lib如下图;

接着配置引用目录,这里需要多配置一个minizip头文件,该头文件是zlib里面的一个子项目。

lib库则需要包含zlibstat.libzlibwapi.lib这两个文件,此处读者可以自行放入到一个目录下;

ZIP 递归压缩目录

如下所示代码是一个使用zlib库实现的简单文件夹压缩工具的C++程序。该程序提供了压缩文件夹到 ZIP 文件的功能,支持递归地添加文件和子文件夹,利用了 Windows API 和 zlib 库的函数。

#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 << "新建缓冲区失败" << 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){// 去掉目录中的.当前目录和..前一个目录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){//注意:处理单独文件的压缩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;
}/*
* 函数功能 : 压缩文件夹到目录
* 备    注 : dirpathName 源文件/文件夹
*      zipFileName 目的压缩包
*      parentdirName 压缩包内名字(文件夹名)
*/
bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
{bool bRet = false;/*APPEND_STATUS_CREATE    创建追加APPEND_STATUS_CREATEAFTER 创建后追加(覆盖方式)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 << "创建ZIP文件失败" << std::endl;return bRet;}if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName)){bRet = true;}zipClose(zFile, NULL);return bRet;
}
主要功能

nyCreateZipfromDir函数

bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);

功能:压缩文件夹到指定的 ZIP 文件。

参数:

  • dirpathName:源文件夹路径。
  • zipfileName:目标 ZIP 文件路径。
  • parentdirName:在 ZIP 文件内的文件夹名(如果为空则不指定目录)。

nyCollectfileInDirtoZip 函数

bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName);

功能:递归地收集文件夹中的文件,并将它们添加到已打开的 ZIP 文件中。

参数:

  • zfile:已打开的 ZIP 文件。
  • filepath:文件夹路径。
  • parentdirName:在 ZIP 文件内的相对文件夹名。

nyAddfiletoZip 函数

bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile);

功能:将指定文件添加到已打开的 ZIP 文件中。

参数:

  • zfile:已打开的 ZIP 文件。
  • fileNameinZip:在 ZIP 文件内的相对文件路径。
  • srcfile:源文件路径。

程序流程

  • 文件夹压缩参数设置: 用户提供源文件夹路径、目标 ZIP 文件路径,以及在 ZIP 文件内的文件夹名。
  • ZIP 文件打开: 根据目标 ZIP 文件是否存在,使用 zipOpen 函数打开 ZIP 文件。
  • 文件夹递归添加: 使用 nyCollectfileInDirtoZip 函数递归地收集文件夹中的文件,并通过 nyAddfiletoZip 函数将它们添加到 ZIP 文件中。
  • ZIP 文件关闭: 使用 zipClose 函数关闭 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");          // 包内文件名<如果为空则压缩时不指定目录>std::cout << "[LyShark] 压缩状态 " << ref << std::endl;system("pause");return 0;
}

上述调用代码,参数1指定为需要压缩的文件目录,参数2指定为需要压缩成目录名,参数3为压缩后该目录的名字。

ZIP 递归解压目录

在这个C++程序中,实现了递归解压缩ZIP文件的功能。程序提供了以下主要功能:

  • replace_all 函数: 用于替换字符串中的指定子串。
  • CreatedMultipleDirectory 函数: 用于创建多级目录,确保解压缩时的目录结构存在。
  • UnzipFile 函数: 用于递归解压缩 ZIP 文件。该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 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")// 将字符串内的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);elsebreak;}return str;
}// 创建多级目录
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;
}/*
* 函数功能 : 递归解压文件目录
* 备    注 : 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 << "数据包: " << 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中解析出来的内部文件名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;// 如果是目录则执行创建递归目录名if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1)){destFilePath = strTempPath + "//" + szZipFName;CreateDirectoryA(destFilePath.c_str(), NULL);}// 如果是文件则解压缩并创建else{// 创建文件 保存完整路径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())){// 将路径格式统一destFilePath = replace_all(destFilePath, "/", "\\");// 创建多级目录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;}// 写入读取的内容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;// 关闭if (unzfile){unzClose(unzfile);}
}
主要功能

replace_all 函数

std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)

功能:在字符串 str 中替换所有的 old_value 为 new_value。

参数:

  • str:待处理的字符串。
  • old_value:要被替换的子串。
  • new_value:替换后的新子串。

返回值:替换后的字符串。

CreatedMultipleDirectory 函数

BOOL CreatedMultipleDirectory(const std::string& direct)

功能:创建多级目录,确保路径存在。

参数:

  • direct:目录路径。
  • 返回值:如果成功创建目录返回 TRUE,否则返回 FALSE。

UnzipFile 函数

void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)

功能:递归解压缩 ZIP 文件。

参数:

  • strFilePath:ZIP 文件路径。
  • strTempPath:解压到的目标路径。

该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 ZIP 文件中的文件或目录。在解析过程中,根据文件或目录的属性,创建相应的目录结构,然后将文件写入目标路径。

示例用法
int main(int argc, char* argv[])
{std::string srcFilePath = "D:\\lyshark\\test.zip";std::string tempdir = "D:\\lyshark\\test";// 如果传入目录不存在则创建if (!::PathFileExistsA(tempdir.c_str())){CreatedMultipleDirectory(tempdir);}// 调用解压函数UnzipFile(srcFilePath, tempdir);system("pause");return 0;
}

案例中,首先在解压缩之前判断传入目录是否存在,如果不存在则需要调用API创建目录,如果存在则直接调用UnzipFIle解压缩函数,实现解包,输出效果图如下;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/159347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bug场景记录

项目场景&#xff1a; mapper.xml文件中sql语句执行失败&#xff0c;显示输入的参数数量不对 问题描述 <select id"page" resultType"com.sky.entity.Employee">select * from employee<where><if test"name ! null and name !"…

ABAP 长文本操作

关联表 1.STXH&#xff1a;长文本抬头表 2.STXL&#xff1a;长文本行表 3.TTXID&#xff1a;Text ID 表 4.TTXOB&#xff1a;Textobject表 5.订单中众多的文本描述&#xff0c;我们怎么知道其对应的【对象】&【ID】呢&#xff1f; 可SE38-通过查找程式&#xff1a;RST…

超详细 | 实验室linux服务器非root账号 | 安装pip | 安装conda

登录实验室公用服务器&#xff0c;个人账号下&#xff08;非root&#xff09;是空的&#xff0c;啥也没有&#xff0c;想安装下pip和conda。 转了一圈&#xff0c;好像没太有针对这个需求写具体博客的&#xff0c;但有挺多讲直接在root下安的&#xff08;用的应该是个人虚拟机&…

基于 EmotiVoice 的批量 TXT 文本转语音工具

比老版本文本转语音更好的TTS工具来了~ &#xff01;&#xff01;&#xff01;除了正常的输入文本转为语音功能之外&#xff0c;还新增了 从 txt 文本批量转为语音的功能。 &#xff01;&#xff01;&#xff01;支持超过 2000 种不同的说话者声音 &#xff01;&#xff01;…

透视未来:现代发电厂地区可视化与智慧能源的结合

随着全球能源消费的不断增长&#xff0c;电力需求也在不断上升。作为能源行业的重要组成部分&#xff0c;现代发电厂扮演着不可替代的角色。而现代发电厂的数据管理和监控系统&#xff0c;则是确保其安全、高效、稳定运行的重要手段。在这个背景下&#xff0c;现代发电厂地区可…

Missing file libarclite_iphoneos.a 问题解决方案

问题 在Xcode 运行项目会报以下错误 File not found: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphoneos.a解决方案 打开URL https://github.com/kamyarelyasi/Libarclite-Files &#xff0c;下载liba…

torchvision中的标准ResNet50网络结构

注&#xff1a;仅用以记录学习 打印出来的网络结构如下&#xff1a; from torchvision import models model models.resnet50(pretrainedFalse) print("model: ", model) 结构&#xff1a; ResNet((conv1): Conv2d(3, 64, kernel_size(7, 7), stride(2, 2), padd…

3.11-容器的资源限制

这一小节我们来看一下如何限制容器的资源&#xff0c;比如CPU和内存。 我们先来看一下对内存的限制。 --memory和--memory-swap这两个参数&#xff0c;如果我们只限定了--memory&#xff0c;没有限定--memory-swap&#xff0c;那么--memory-swap的大小就会和--memory大小一样。…

go语言学习-基本概念与流程控制

1、hello world // package: 声明这个文件是属于哪个包的 // 什么是包&#xff1a;可以理解为Go源码的集合&#xff0c;也是一种比较高级的代码复用方案。 // 我们可以把一些复用的代码或者是功能封装在一起&#xff0c;然后形成一个包&#xff0c;可以被另外一个包进行引用&a…

瞻博 Juniper 网络设备基线安全加固操作

目录 账号管理、认证授权账号 ELK-Juniper-01-01-01 口令 ELK-Juniper-01-02-01 ​​​​​​​认证 ELK-Juniper-01-03-01 日志配置 通信协议 ELK-Juniper-03-01-01 设备其他安全要求 ELK-Juniper-04-01-01 账号管理、认证授权账号 ELK-Juniper-01-01-01 编号&#xff1a…

C#实现批量生成二维码

相信大家都使用过草料二维码生成器&#xff0c;单独生成二维码可以&#xff0c;但是批量生成二维码就需要收费了。既然要收费&#xff0c;那就自己写一个。 接口采用导入Excel文件生成二维码&#xff0c;首先需要读取Excel的数据&#xff0c;方法如下所示&#xff1a; /// <…

python练习题(markdown中的60道题)

1.Demo01 摄氏温度转化为华氏温度 celsius float(input(输入摄氏温度&#xff1a;)) fahrenheit (9/5)*celsius 32 print(%0.1f 摄氏温度转为华氏温度为 %0.1f % (celsius, fahrenheit))结果&#xff1a; 2.Demo02 计算圆柱体的体积 h, r map(float, input().split())# …

python tkinter 使用(三)

python tkinter 使用(三) 本篇文章主要讲下tkinter下的filedialog的使用. 1: askopenfilename 首先使用tkinter中fiedialog来实现一个简单的文件选择器. 这里使用askopenfilename()来启动文件选择器,选择成功后打印下所选文件的名称. #!/usr/bin/python3 # -*- coding: UT…

echarts实现如下图功能代码

这里写自定义目录标题 const option {tooltip: {trigger: axis},legend: {left: "-1px",top:15px,type: "scroll",icon:rect,data: [{name:1, textStyle:{color: theme?"#E5EAF3":#303133,fontSize:14}}, {name: 2, textStyle:{color: theme…

小程序泄露腾讯地图apikey

今天挖小程序时测了很久&#xff0c;一直没有头绪&#xff0c;后来想要测试一下支付漏洞&#xff0c;但是这里却出问题了 添加地址时我发现&#xff0c;当我添加一个地址时&#xff0c;他会显示腾讯地图的logo和一部分小图&#xff0c;那时候我就在想&#xff0c;既然这里可以调…

函数隐式声明的危害及处理(C语言)

1.为什么要将函数隐式声明提升为编译错误 请看这个编译警告&#xff1a; [ 71%] Building C object main/CMakeFiles/shake.dir/pjt_helper.c.o /home/fengxh/shake_src/data_dispatch/main/pjt_helper.c: In function ‘SysMqtt_GetStateJsonPayload’: /home/fengxh/shake_s…

Vmware 扩展硬盘空间后的操作-Ubuntu

在VMware中扩展了Ubuntu虚拟机的硬盘容量后&#xff0c;你需要在Ubuntu内部进行操作才能使用新增的空间。过程包括为增加的空间建立分区、格式化以及挂载该分区供使用。下面是具体的步骤&#xff1a; 首先登录到你的Ubuntu系统&#xff0c;用lsblk命令查看分区情况。这样你可以…

多普勒流速仪的功能作用是什么?

我国地域广大&#xff0c;各地降雨分布不均&#xff0c;某些城市经常会出现连续的降雨进而导致城市排水压力过大&#xff0c;为了提高城市应对排水过量的极端情况的出现&#xff0c;亟需一种方案能够对城市排水进行有效及时的监测&#xff0c;从而能够及时的采取应对方案。 在污…

IO口电压下降那么多是怎么回事??

前几天一个工程师向我反馈他测得如下电路MCU IO口的电压不是3.3V&#xff0c;只有2V多。 IO配置的是输入功能&#xff0c;无上下拉。最初我不太相信这个结果&#xff0c;后来自己用万用表实际测量了下&#xff0c;还真是这个结果 这是咋回事呢&#xff1f;不应该电压就是3.3V吗…

前缀和的动态维护——树状数组[C/C++]

文章目录 前言lowbitlowbit的定义lowbit的计算 树状数组的思想树状数组的操作单点修改 update前缀查询 query树状数组的建立 build 前言 树状数组巧妙了利用位运算和树形结构实现了允许单点修改的情况下&#xff0c;动态维护前缀和&#xff0c;并且实现单点修改和前缀和查询的效…