C/C++ Zlib实现文件压缩与解压

在软件开发和数据处理中,对数据进行高效的压缩和解压缩是一项重要的任务。这不仅有助于减小数据在网络传输和存储中的占用空间,还能提高系统的性能和响应速度。本文将介绍如何使用 zlib 库进行数据的压缩和解压缩,以及如何保存和读取压缩后的文件。zlib 是一个开源的数据压缩库,旨在提供高效、轻量级的压缩和解压缩算法。其核心压缩算法基于 DEFLATE,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。zlib 库广泛应用于多个领域,包括网络通信、文件压缩、数据库系统等。

保存文件

使用 CreateFile 打开文件,通过 WriteFile 向文件中写出数据,最后调用 CloseHandle 关闭句柄,实现文件的保存。

#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 库提供的用于数据压缩的函数,通过该函数可以将数据进行压缩。下面是一个示例,演示了如何使用 zlib 库进行文件压缩。

它的原型如下:

int compress(Bytef* dest, uLongf* destLen, const Bytef* source, uLong sourceLen);
  • dest:指向存放压缩后数据的缓冲区的指针。
  • destLen:传入时为压缩缓冲区的大小,传出时为实际压缩后数据的大小。
  • source:指向待压缩数据的缓冲区的指针。
  • sourceLen:待压缩数据的大小。

compress 函数的作用是将 source 指向的数据进行压缩,并将结果存放在 dest 指向的缓冲区中。destLen 传入时应该是 dest 缓冲区的大小,函数执行后,destLen 会更新为实际压缩后数据的大小。

函数返回值为压缩的执行状态,可能的返回值包括:

  • Z_OK:压缩成功。
  • Z_MEM_ERROR:内存分配失败。
  • Z_BUF_ERROR:压缩输出缓冲区不足。

这个函数实际上是使用 DEFLATE 算法进行压缩,DEFLATE 是一种通用的压缩算法,也是 zlib 库的核心算法之一。压缩后的数据可以使用 uncompress 函数进行解压缩。

总体而言,compress 函数提供了一种简单的方式,可以在应用程序中对数据进行压缩,适用于需要减小数据体积的场景,比如网络传输或数据存储。

// 单个文件限制大小为 100M 
#define MAX_SRC_FILE_SIZE (100*1024*1024)/*** @brief 压缩指定文件的数据** @param pszCompressFileName 待压缩文件的路径* @param ppCompressData 保存压缩后数据的指针* @param pdwCompressDataSize 传入时为压缩缓冲区的大小,传出时为实际压缩后数据的大小* @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;// 分配源数据和目标数据的内存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;// 读取源数据ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL);// 检查读取是否成功if ((0 >= dwRet) || (dwRet != dwFileSize)){delete[] pDestData;delete[] pSrcData;CloseHandle(hFile);return FALSE;}int iRet = 0;// 压缩数据do{iRet = compress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);// 压缩成功,退出循环if (0 == iRet){break;}// 输出缓冲区不足,增加缓冲区大小并重试else if (-5 == iRet){delete[] pDestData;pDestData = NULL;dwDestDataSize = dwDestDataSize + (100 * 1024);pDestData = new BYTE[dwDestDataSize];// 分配新的目标数据内存if (NULL == pDestData){delete[] pSrcData;CloseHandle(hFile);return FALSE;}}// 压缩失败,释放内存并返回失败else{delete[] pDestData;pDestData = NULL;delete[] pSrcData;pSrcData = NULL;CloseHandle(hFile);return FALSE;}} while (TRUE);// 保存压缩后数据的指针和实际大小*ppCompressData = pDestData;*pdwCompressDataSize = dwDestDataSize;// 释放源数据内存delete[] pSrcData;// 关闭文件句柄CloseHandle(hFile);// 返回压缩成功return TRUE;
}

文件解压缩

uncompress 函数是 zlib 库提供的用于数据解压缩的函数,通过该函数可以将压缩后的数据解压缩还原。下面是一个示例,演示了如何使用 zlib 库进行文件解压缩。

它的原型如下:

int uncompress(Bytef* dest, uLongf* destLen, const Bytef* source, uLong sourceLen);
  • dest:指向存放解压缩后数据的缓冲区的指针。
  • destLen:传入时为解压缩缓冲区的大小,传出时为实际解压缩后数据的大小。
  • source:指向待解压缩数据的缓冲区的指针。
  • sourceLen:待解压缩数据的大小。

uncompress 函数的作用是将 source 指向的数据进行解压缩,并将结果存放在 dest 指向的缓冲区中。destLen 传入时应该是 dest 缓冲区的大小,函数执行后,destLen 会更新为实际解压缩后数据的大小。

函数返回值为解压缩的执行状态,可能的返回值包括:

  • Z_OK:解压缩成功。
  • Z_MEM_ERROR:内存分配失败。
  • Z_BUF_ERROR:解压缩输出缓冲区不足。
  • Z_DATA_ERROR:输入数据错误或损坏。

uncompress 函数实际上是使用 DEFLATE 算法进行解压缩,与 compress 函数相对应。这两个函数共同构成了 zlib 库中的基本数据压缩和解压缩功能。

在实际应用中,可以使用这两个函数来处理需要压缩和解压缩的数据,例如在网络通信中减小数据传输量或在存储数据时减小占用空间。

/*** @brief 解压指定文件的数据** @param pszUncompressFileName 待解压文件的路径* @param ppUncompressData 保存解压后数据的指针* @param pdwUncompressDataSize 传入时为解压缓冲区的大小,传出时为实际解压后数据的大小* @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);// 设置目标数据缓冲区大小DWORD dwDestDataSize = MAX_SRC_FILE_SIZE;// 分配源数据和目标数据的内存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;// 读取源数据ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL);// 检查读取是否成功if ((0 >= dwRet) || (dwRet != dwFileSize)){delete[] pDestData;delete[] pSrcData;CloseHandle(hFile);return FALSE;}int iRet = 0;// 解压缩数据do{iRet = uncompress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);// 解压缩成功,退出循环if (0 == iRet){break;}// 输出缓冲区不足,增加缓冲区大小并重试else if (-5 == iRet){delete[] pDestData;pDestData = NULL;dwDestDataSize = dwDestDataSize + (100 * 1024);pDestData = new BYTE[dwDestDataSize];// 分配新的目标数据内存if (NULL == pDestData){delete[] pSrcData;CloseHandle(hFile);return FALSE;}}// 解压缩失败,释放内存并返回失败else{delete[] pDestData;pDestData = NULL;delete[] pSrcData;pSrcData = NULL;CloseHandle(hFile);return FALSE;}} while (TRUE);// 保存解压后数据的指针和实际大小*ppUncompressData = pDestData;*pdwUncompressDataSize = dwDestDataSize;// 释放源数据内存delete[] pSrcData;// 关闭文件句柄CloseHandle(hFile);// 返回解压成功return TRUE;
}

演示示例

下面是一个包含文件压缩和解压缩的完整示例,展示了如何将文件进行压缩保存,然后解压还原。

调用CompressData压缩文件,返回结果pCompressData存放文件内存字节,结果dwCompressDataSize存放长度,并调用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;}// 保存压缩数据为文件bRet = SaveToFile("d:\\text.zlib", pCompressData, dwCompressDataSize);if (TRUE == bRet){std::cout << "已保存到文件" << std::endl;}// 释放内存delete[]pCompressData;pCompressData = NULL;system("pause");return 0;
}

调用UncompressData解压缩文件,返回结果pUncompressData存放文件内存字节,结果dwUncompressDataSize存放长度,并调用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;}// 保存解压数据为文件bRet = SaveToFile("d:\\test.exe", pUncompressData, dwUncompressDataSize);if (TRUE == bRet){std::cout << "已保存到文件" << std::endl;}// 释放内存delete[]pUncompressData;pUncompressData = NULL;system("pause");return 0;
}

编译时可能会提示无法生成SAFESEH影响的报错信息,如下图所示;

此时打开项目属性页,找到链接器,高级选项卡,将映像安全处理改为否即可,如下图所示;

结论

通过使用 zlib 库,我们可以方便地在应用程序中实现数据的压缩和解压缩功能。这对于需要减小数据传输量或在存储数据时减小占用空间的场景非常有用。在实际应用中,可以根据需要调整缓冲区大小和处理流程,以适应不同的数据处理需求。

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

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

相关文章

【开源】基于Vue和SpringBoot的数字化社区网格管理系统

项目编号&#xff1a; S 042 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S042&#xff0c;文末获取源码。} 项目编号&#xff1a;S042&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、开发背景四、系统展示五、核心源码5…

Android 掉帧优化

对于传统的60刷新率手机来说&#xff0c;每16ms会发出一个VSync信号&#xff0c;复制CPU/GPU放在缓存中的图像&#xff0c;再通知CPU/GPU计算下一帧要显示的内容&#xff0c;再把刚复制的图像显示在屏幕上&#xff0c;这就是一个屏幕刷新周期。而如果在16ms内没有计算完毕的话&…

六、源NAT实验

学习防火墙之前&#xff0c;对路由交换应要有一定的认识 源NAT1.私网用户通过NAT No-PAT访问Internet2.私网用户通过NATP访问Internet3.私网用户通过Easy-IP访问Internet4.私网用户通过三元组NAT访问Internet5.双出口环境下私网用户通过NAPT访问Internet 源NAT ———————…

预览功能实现

需求&#xff1a;将后端返回来的文字或者图片和视频展示在页面上。 <!-- 预览 --><el-dialog title"预览" :visible.sync"dialogPreviewVisible" width"50%" append-to-body :close-on-click-modal"false" close"Previe…

微服务--03--OpenFeign 实现远程调用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 OpenFeign其作用就是基于SpringMVC的常见注解&#xff0c;帮我们优雅的实现http请求的发送。 RestTemplate实现了服务的远程调用 OpenFeign快速入门1.引入依赖2.启用…

js手写数组push(),unshift(),pop(),shift(),map()方法

目录 1、push() 2、unshift() 3、pop() 4、shift() 5、map() 1、push() Array.prototype.pushfunction(){for(let i0;i<arguments.length;i){this[this.length]arguments[i]}return this.length}const arr[1,2,3]console.log(arr.push(4,5,6)) 2、unshift() Array.prot…

Docke日常指令

本文针对ubuntu操作系统而言&#xff1a; 补充&#xff1a;1.XAhost命令是X服务器的访问控制工具&#xff0c;用来控制哪些X客户端能够在X服务器上显示。 2.容器与镜像之间的关系&#xff1a;镜像你可以把它看成Java中的类&#xff0c;而容器可以看做是类的实例化对象&#xf…

OpenCV快速入门【完结】:总目录——初窥计算机视觉

文章目录 前言目录1. OpenCV快速入门&#xff1a;初探2. OpenCV快速入门&#xff1a;像素操作和图像变换3. OpenCV快速入门&#xff1a;绘制图形、图像金字塔和感兴趣区域4. OpenCV快速入门&#xff1a;图像滤波与边缘检测5. OpenCV快速入门&#xff1a;图像形态学操作6. OpenC…

创建可以离线打包开发的uniapp H5项目

安装node环境 略 安装vue脚手架&#xff0c;在线 npm install -g vue/cli PS&#xff1a;vue-cli已进入维护模式&#xff0c;vue3最新脚手架使用npm init vuelatest安装&#xff0c;安装后使用create-vue替换vue指令&#xff0c;create-vue底层使用vite提升前端开发效率&…

Redis应用的16个场景

常见的16种应用场景: 缓存、数据共享分布式、分布式锁、全局 ID、计数器、限流、位统计、购物车、用户消息时间线 timeline、消息队列、抽奖、点赞、签到、打卡、商品标签、商品筛选、用户关注、推荐模型、排行榜. 1、缓存 String类型 例如&#xff1a;热点数据缓存&#x…

Redis 命令处理过程

我们知道 Redis 是一个基于内存的高性能键值数据库, 它支持多种数据结构, 提供了丰富的命令, 可以用来实现缓存、消息队列、分布式锁等功能。 而在享受 Redis 带来的种种好处时, 是否曾好奇过 Redis 是如何处理我们发往它的命令的呢&#xff1f; 本文将以伪代码的形式简单分析…

【git】工作中常用的命令

前言 一些工作学习中常用的git命令小合集 正文 git clone git clone 使用的账号密码 用https的链接&#xff0c;就是要用github/gitee对应的用户名和密码。 git clone 获取指定指定分支的指定commit版本 第一步&#xff1a; git clone [git-url] -b [branch-name] 第二步…

centos 显卡驱动安装(chatglm2大模型安装步骤一)

1.服务器配置 服务器系统:Centos7.9 x64 显卡:RTX3090 (24G) 2.安装环境 2.1 检查显卡驱动是否安装 输入命令:nvidia-smi(显示显卡信息) 如果有以下显示说明,已经有显卡驱动。否则需要重装。 2.2 下载显卡驱动 第一步:浏览器输入https://www.nvidia.cn/Downloa…

Python读取Ansible playbooks返回信息

一&#xff0e;背景及概要设计 当公司管理维护的服务器到达一定规模后&#xff0c;就必然借助远程自动化运维工具&#xff0c;而ansible是其中备选之一。Ansible基于Python开发&#xff0c;集合了众多运维工具&#xff08;puppet、chef、func、fabric&#xff09;的优点&#x…

浅谈C++中如何重载前置++/--与后置++/--

前置/–与后置/– C中的和–操作符存在前置式与后置式&#xff0c;最基本的&#xff0c;作为一名程序员&#xff0c;你应当了解它们实现的不同&#xff1a; i; i;以上两行代码如果单独使用&#xff0c;在功能上是一致的&#xff0c;它们都实现了让变量i加1的操作&#xff0c;…

nuxt、vue实现PDF和视频文件的上传、下载、预览

上传 上传页面 <el-form-item :label"(form.ququ3 1 ? 参培 : form.ququ3 2 ? 授课 : ) 证明材料" prop"ququ6"><PdfUpload v-model"form.ququ6" :fileType"[pdf, mp4, avi, ts]"></PdfUpload> </el-form-i…

python:使用for循环与while循环打印九九乘法表

python&#xff1a;使用for循环与while循环打印九九乘法表 在编程中&#xff0c;for循环和while循环是两种常用的循环结构&#xff0c;它们可以用来实现各种不同的功能和逻辑。其中&#xff0c;九九乘法表是一个经典的例子&#xff0c;可以用来展示for循环和while循环的使用方…

亚马逊,shein,temu如何避免爆品评分低被强制下架

近期&#xff0c;一些Temu卖家反映产品下架问题&#xff0c;无论是日出千单的爆品还是其他商品&#xff0c;都有可能面临下架的风险。这其中最主要的原因之一是产品质量问题&#xff0c;导致消费者差评较多&#xff0c;评分降至4.2分或4.0分以下时&#xff0c;平台可能会强制下…

EfficientViT:具有级联群体注意力的内存高效Transformer

EfficientViT: Memory Efficient Vision Transformer with Cascaded Group Attention 1、介绍2、使用 Vision Transformer 加快速度2.1 内存效率2.2 计算效率2.3 参数效率 3、Efficient Vision Transformer3.1 EfficientViT 构建模块3.3 EfficientViT 网络架构 4、实验5、结论 …

YOLOv8独家原创改进: AKConv(可改变核卷积),即插即用的卷积,效果秒杀DSConv | 2023年11月最新发表

💡💡💡本文全网首发独家改进:可改变核卷积(AKConv),赋予卷积核任意数量的参数和任意采样形状,为网络开销和性能之间的权衡提供更丰富的选择,解决具有固定样本形状和正方形的卷积核不能很好地适应不断变化的目标的问题点,效果秒殺DSConv 1)AKConv替代标准卷积进行…