C++ MiniZip实现目录压缩与解压的示例详解

 更新时间:2023年11月22日 17:15:34   作者:微软技术分享  
Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法,本文主要为大家详细介绍了如何利用Zlib实现目录压缩与解压,需要的小伙伴可以参考下

脚本之家 / 编程助手:解决程序员“几乎”所有问题!
脚本之家官方知识库 → 点击立即使用

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

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

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

编译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 库的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#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函数

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

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

参数:

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

nyCollectfileInDirtoZip 函数

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

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

参数:

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

nyAddfiletoZip 函数

1
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 文件。

示例用法

1
2
3
4
5
6
7
8
9
10
11
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 文件中的文件或目录。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#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);
        else
            break;
    }
    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 函数

1
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 函数

1
BOOL CreatedMultipleDirectory(const std::string& direct)

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

参数:

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

UnzipFile 函数

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

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

参数:

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

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

示例用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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解压缩函数,实现解包,输出效果图如下;

到此这篇关于C++ MiniZip实现目录压缩与解压的示例详解的文章就介绍到这了,更多相关C++ MiniZip目录压缩与解压内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://www.cnblogs.com/LyShark/p/17849090.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • C++11的functional模块介绍和使用案例

    C++11的functional模块介绍和使用案例

    functional模块是C++ 11提供了一组函数对象和算法,用于增强C++的函数式编程能力,本文主要介绍了C++11的functional模块介绍和使用案例,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • C语言之把数组名作函数参数的四种情况说明

    C语言之把数组名作函数参数的四种情况说明

    这篇文章主要介绍了C语言之把数组名作函数参数的四种情况说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • 深入理解c++指针的指针和指针的引用

    深入理解c++指针的指针和指针的引用

    下面小编就为大家带来一篇深入理解c++指针的指针和指针的引用。小编觉得挺不错的,现在就分享给大家,也给大家做个参考,一起跟随小编过来看看吧
    2016-06-06
  • VS+QT编译环境中字符乱码问题解决方法

    VS+QT编译环境中字符乱码问题解决方法

    编码就是把⼀个字符编码成二进制码存起来的方式,而解码就是把这个二进制码按照原本编码的规则还原成原来的字符,这篇文章主要介绍了VS+QT编译环境中字符乱码问题详解,需要的朋友可以参考下
    2024-01-01
  • C++基础教程之指针拷贝详解

    C++基础教程之指针拷贝详解

    这篇文章主要介绍了C++基础教程之指针拷贝详解的相关资料,需要的朋友可以参考下
    2017-01-01
  • 关于C++类的成员初始化列表的相关问题

    关于C++类的成员初始化列表的相关问题

    下面小编就为大家带来一篇关于C++类的成员初始化列表的相关问题。小编觉得挺
    2016-05-05
  • C语言中的abs()函数和exp()函数的用法

    C语言中的abs()函数和exp()函数的用法

    这篇文章主要介绍了C语言中的abs()函数和exp()函数的用法,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2015-08-08
  • C语言中队列的结构和函数接口的使用示例

    C语言中队列的结构和函数接口的使用示例

    队列只允许一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO的性质;队列可用数组和链表 的方法实现,使用链表的结构实现更优一些,因为如果使用数组节,出队列时删去首元素需要将整个数组前移,效率比较低
    2023-02-02
  • C语言详解float类型在内存中的存储方式

    C语言详解float类型在内存中的存储方式

    在c语言中float函数是单精度的。它在内存中以二进制的形式存储。分为符号位,阶码与尾数三部分,下面我们详细来了解一下
    2022-04-04
  • C++采用ring3读取MBR实例

    C++采用ring3读取MBR实例

    这篇文章主要介绍了C++采用ring3读取MBR实例,可实现对硬盘的主引导记录的读取,非常具有实用价值,需要的朋友可以参考下
    2014-10-10

最新评论