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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2、XFP 与 SFP+:有什么区别?

在光纤网络领域&#xff0c;光模块是促进数据顺利传输的重要组件。市场继续接受10G XFP和10G SFP等10G光模块&#xff0c;促使人们对XFP与SFP进行更仔细的审视。他们有什么区别&#xff1f;XFP和SFP的定义是什么&#xff1f;他们的应用场景又如何呢&#xff1f;在下文中寻找所有…

MySQL用得好好的,为何要转ES?

MySQL是一种关系型数据库&#xff0c;它可以高效地存储和查询结构化的数据。 ES是一种分布式搜索引擎&#xff0c;它可以快速地对海量的非结构化或半结构化的数据进行全文检索和分析。 MySQL 和 ES 的数据存储方式也不同。MySQL 中的数据通常是以关系型表的形式存储在磁盘上&…

8.0 泛型

通过之前的学习&#xff0c;读者可以了解到&#xff0c;把一个对象存入集合后&#xff0c;再次取出该对象时&#xff0c;该对象的编译类型就变成了Object类型&#xff08;尽管其在运行时类型没有改变&#xff09;。集合设计成这样&#xff0c;提高了它的通用性&#xff0c;但是…

MySQL(免密登录)

简介: MySQL免密登录是一种允许用户在没有输入密码的情况下直接登录到MySQL服务器的配置。这通常是通过在登录时跳过密码验证来实现的。 1、修改MySQL的配置文件 使用vi /etc/my.cnf&#xff0c;添加到【mysqld】后面 skip-grant-tables #配置项告诉mysql跳过权限验证&#…

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

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

【计算机组成原理】存储系统

&#x1f384;欢迎来到边境矢梦的csdn博文&#x1f384; &#x1f384;本文主要梳理计算机组成原理中 存储系统的知识点和值得注意的地方 &#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以…

Docker可视化工具Portainer(轻量)或者Docker容器监控之 CAdvisor+InfluxDB+Granfana(重量)

Docker轻量级可视化工具Portainer 是什么 Portainer 是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 安装 官网 https://www.portainer.io/ https://docs.portainer.io/v/ce-2.9/start/instal…

【密码学】【安全多方计算】浅析隐私求交PSI

文章目录 隐私求交的定义隐私求交方案介绍1. 基于DH的PSI方案2. 基于OT的PSI方案3.基于OPRF的PSI方案 总结 隐私求交的定义 隐私集合求交使得持有数据参与方通过计算得到集合的交集数据&#xff0c;而不泄露任何交集以外的数据信息。 隐私求交方案介绍 1. 基于DH的PSI方案 …

如何通过内网穿透实现公网远程ssh连接kali系统

文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过[cpolar 内网穿透](cpolar官网-安全的内网穿透工具 | 无需公网ip | 远程访问 | 搭建网站)软件实现ssh远程连接kali 1…

docker安装Sentinel

文章目录 引言I Sentinel安装1.1 运行容器1.2 DOCKERFILE 参考1.3 pom 依赖1.4 .yml配置(整合springboot)II 资源保护2.1 Feign整合Sentinel2.2 CommonExceptionAdvice:限流异常处理类引言 I Sentinel安装 Sentinel 分为两个部分: 核心库(Java 客户端)不依赖任何框架/库,能…

了解静态测试?

静态测试是一种软件测试方法&#xff0c;它主要通过分析软件或代码的静态属性来检查潜在的问题和缺陷&#xff0c;而无需实际执行程序。这种测试方法侧重于检查源代码和其他软件文档&#xff0c;以发现错误并提高软件质量。 为什么要做静态测试&#xff1f; 提前发现和修复错…

ESP32-Web-Server编程-CSS 基础 2

ESP32-Web-Server编程-CSS 基础 2 概述 如上节所述&#xff0c;可以使用外部 CSS 文件来修饰指定的 HTML 文件。 外部引用 - 使用外部 CSS 文件。 当样式需要被应用到很多页面的时候&#xff0c;外部样式表将是理想的选择。使用外部样式表&#xff0c;就可以通过更改一个文件…

Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/134561660 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

Vue 实现低代码开发平台,没想到这么好用!

前言 在众多开发技术中&#xff0c;Vue 组件化开发技术以其卓越的灵活性和高效性备受瞩目。 低代码平台相信不少人知道它的存在&#xff0c;而且现在大部分公司都在开发自己的低代码平台&#xff0c;首先我们来看看低代码平台可视化界面&#xff1a; 官网&#xff1a;https:/…

MySQL中自增id用完怎么办?

MySQL中自增id用完怎么办&#xff1f; MySQL里有很多自增的id&#xff0c;每个自增id都是定义了初始值&#xff0c;然后不停地往上加步长。虽然自然数是没有上限的&#xff0c;但是在计算机里&#xff0c;只要定义了表示这个数的字节长度&#xff0c;那它就有上限。比如&#…

第二十章,多线程

创建线程 有两种方式&#xff0c;分别为继承Java.lang.Thread类与实现Java.lang.Runnable接口 继承Thread类 Thread常用的两个构造方法语法 public Thread&#xff08;&#xff09;&#xff1b; public Thread&#xff08;String threadName&#xff09;&#xff1b; 继承…

新生儿腺体肥大:原因、科普和注意事项

引言&#xff1a; 新生儿的健康问题常常让父母感到焦虑&#xff0c;其中之一就是腺体肥大。了解腺体肥大的原因、科普相关知识&#xff0c;并采取一些建议的注意事项&#xff0c;对于保障新生儿的健康非常重要。本文将深入解析新生儿腺体肥大的原因、提供相关科普知识&#xf…

可移动框 弹窗 可拖拽的组件

电脑端: <template><divv-if"show"ref"infoBox"mousedown.stop"mouseDownHandler"class"info-box":style"styleObject"><slot></slot></div> </template> <script> export defa…

自媒体爆文采集工具,几个免费的网站让你每日爆文增加

随着自媒体的蓬勃发展&#xff0c;许多人憧憬着在这个领域获得成功和流行。然而&#xff0c;随着寒冷的冬天的降临&#xff0c;媒体从业者的日常生活并没有变得更加美好。在竞争激烈的环境中&#xff0c;为了生存&#xff0c;他们必须发布引人注目的内容&#xff0c;然而&#…

4个Python实战项目,让你瞬间读懂Python!

前言 Python 是一种极具可读性和通用性的编程语言。Python 这个名字的灵感来自于英国喜剧团体 Monty Python&#xff0c;它的开发团队有一个重要的基础目标&#xff0c;就是使语言使用起来很有趣。Python 易于设置&#xff0c;并且是用相对直接的风格来编写&#xff0c;对错误…