首页 > 编程语言 >C/C++ Zlib实现文件压缩与解压

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

时间:2023-11-28 10:36:45浏览次数:36  
标签:解压 return 压缩 Zlib 解压缩 C++ NULL pDestData hFile

在软件开发和数据处理中,对数据进行高效的压缩和解压缩是一项重要的任务。这不仅有助于减小数据在网络传输和存储中的占用空间,还能提高系统的性能和响应速度。本文将介绍如何使用 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 库,我们可以方便地在应用程序中实现数据的压缩和解压缩功能。这对于需要减小数据传输量或在存储数据时减小占用空间的场景非常有用。在实际应用中,可以根据需要调整缓冲区大小和处理流程,以适应不同的数据处理需求。

标签:解压,return,压缩,Zlib,解压缩,C++,NULL,pDestData,hFile
From: https://www.cnblogs.com/LyShark/p/17861293.html

相关文章

  • Linux文件查找,打包,压缩及解压
    1.find命令:2.find命令用于在文件系统中搜索文件和目录。3.例如,要在/home目录下查找所有以.txt结尾的文件,可以使用:find/home-name"*.txt"。4.grep命令:5.grep命令用于在文件中搜索特定模式。6.例如,要在当前目录下的所有文件中查找包含"keyword"的行,可以使用:grep"keyw......
  • C++ 十进制与十六进制转换
    文章作者:里海十进制与十六进制转换#include<iostream>#include<string>usingnamespacestd;//十进制整数转十六进制字符串stringDecimalToHex(longlongdecimal){stringhex="";while(decimal>0){intremainder=decimal%16;......
  • C++ 查找文本文件中字符串是否存在
    简介查找文本文件中字符串是否存在代码#include<iostream>#include<fstream>#include<vector>#include<string>usingnamespacestd;boolSearchString(stringfilePath,stringstrF){vector<string>lines;stringline;ifst......
  • C\C++ 使用RapidJSON库,轻松解析和生成JSON
    简介  RapidJSON是一个高效的C++JSON解析器和生成器。它专注于性能和易用性,使得处理JSON数据变得简单和快速。RapidJSON支持现代的JSON特性,如嵌套对象、数组、Unicode编码和注释。它的API简洁易用,可以轻松解析和生成JSON数据。无论你的项目需要处理大量的JSON数据,还是只需要解析......
  • C++ 字符串编码转换封装函数,UTF-8编码与本地编码互转
    简介字符串编码转换封装函数,UTF-8编码与本地编码互转。中文乱码的解决方法有时候我们会遇到乱码的字符串,比如:古文码可能是用GBK方式读取UTF-8编码的中文导致的,用下面的Utf8ToLocal(stringstr)函数转换一下就可以了。口字码可能是因为以UTF-8的方式读取GBK编码的中文导致的,用下面......
  • C++ 01.学习C++的意义-狄泰软件学院
    一些历史UNIX操作系统诞生之初是用汇编语言编写的随着UNIX系统的发展,汇编语言的开发效率成为瓶颈,所以需要一个新的语言替代汇编语言1971年通过对B语言改良,使其能直接产生机器代码,C语言诞生UNIX使用C语言重写,同时C语言在实践中不断升级完善。C语言的特点没有深思熟虑的设计过程残留......
  • C++ 修改文件创建时间、修改时间属性
    简介        修改文件创建时间、修改时间、大小等属性。        博客《C++获取文件创建时间、修改时间、大小等属性》分享后,好兄弟“古月”发来一段代码,说可以修改文件的创建时间等。测试了一下真可以,下面是运行效果和代码:代码#include<windows.h>#include<f......
  • C++ 使用Windows的API CreateDirectory 创建多层级文件夹
    简介使用Windows的API创建多层级文件夹效果代码#include<windows.h>#include<direct.h>#include<iostream>#include<string>#include<sstream>#include<vector>//创建多层级文件夹boolCreateDir(conststd::string&path){ std::......
  • C++ 获取文件创建时间、修改时间、大小等属性
    简介获取文件创建时间、修改时间、大小等属性代码#include<iostream>#include<string.h>#include<time.h>voidmain(){std::stringfilename="E:\\LiHai123.txt";struct_statstat_buffer;intresult=_stat(filename.c_str(),&stat_b......
  • C++ 获取网卡名称和IP地址
    描述这是获取网卡名称和IP地址的代码示例,参考自。原文描述得比较详细,感谢博主分享。原文代码中没有输出网卡的物理地址,下面的代码进行了补充,并在win10上运行正常。代码//#include<WinSock2.h>#include<Iphlpapi.h>#pragmacomment(lib,"Iphlpapi.lib")//需要添加Iphlpapi.lib......