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

C/C++ Zlib实现文件压缩与解压的更多相关文章

  1. CSharp tar类型文件压缩与解压

    最近闲暇时间开始写点通用基础类在写到tar类型文件压缩与解压时遇到点问题 压缩用的类库我是下载的 SharpZipLib_0860版本 先上代码 加压核心 /// <summary> // ...

  2. Linux之文件压缩与解压

    文件压缩与解压 1.tar命令 tar命令可以为Linux的文件和目录创建档案. 利用tar,可以为某一特定文件创建档案(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件.tar最初被用来 ...

  3. I/O操作之文件压缩与解压

    与文件压缩与解压相关的类在java.util.zip包下 实例 //文件压缩 import java.io.File; import java.io.FileInputStream; import j ...

  4. 文件压缩、解压工具类。文件压缩格式为zip

    package com.JUtils.file; import java.io.BufferedOutputStream; import java.io.File; import java.io.Fi ...

  5. 文件压缩跟解压(本地&Linux服务器)

    远程解压需要的jar包: <dependency> <groupId>commons-net</groupId> <artifactId>commons ...

  6. Java实现文件压缩与解压

    Java实现ZIP的解压与压缩功能基本都是使用了Java的多肽和递归技术,可以对单个文件和任意级联文件夹进行压缩和解压,对于一些初学者来说是个很不错的实例.(转载自http://www.puiedu. ...

  7. linux下文件压缩与解压操作

    对于刚刚接触Linux的人来说,一定会给Linux下一大堆各式各样的文件名给搞晕.别个不说,单单就压缩文件为例,我们知道在Windows下最常见的压缩文件就只有两种,一是,zip,另一个是.rap.可 ...

  8. C# 文件压缩与解压(ZIP格式)

    在企业开发过程中经常会遇到文件的压缩与解压,虽然网上很多流行的压缩文件格式都是RAR的,但是由于RAR不是一个开放的标准,因此ZIP成了更多人的选择.如果你不想自己开发的话可以选择开源的项目,比如Sh ...

  9. Java实现文件压缩与解压[zip格式,gzip格式]

    Java实现ZIP的解压与压缩功能基本都是使用了Java的多肽和递归技术,可以对单个文件和任意级联文件夹进行压缩和解压,对于一些初学者来说是个很不错的实例. zip扮演着归档和压缩两个角色:gzip并 ...

  10. 16 Linux系统的文件压缩、解压与归档

    这一节的内容,我们详细介绍下Linux的文件压缩.解压缩与文件归档的内容,也就是tar.gzip.bzip2.xz等命令的内容: 压缩(compress)与解压缩(uncompress) Linux系 ...

随机推荐

  1. VMware15.5安装Ubuntu20.04

    一.安装前的准备 1.下载好Ubuntu20.04的镜像文件,直接从官网下载就好,激活密匙. 2.准备好VMware软件,这里就忽略安装过程了. 二.建立虚拟机以及开启正式的Ubuntu安装过程 参考 ...

  2. 一、swift对象存储环境搭建

    系列导航 一.swift对象存储环境搭建 二.swift添加存储策略 三.swift大对象--动态大对象 四.swift大对象--静态态大对象 五.java操作swift对象存储(官网样例) 六.ja ...

  3. 启动vue项目失败,报错Failed at the node-sass@4.14.1 postinstall script.

    https://www.cnblogs.com/xiaodangshan/p/13061618.html

  4. COM组件开发-关于在开发环境下COM组件的(来自 HRESULT 的异常:0x80080005 (CO_E_SERVER_EXEC_FAILURE)) 以及 在CLR语言下可能报错 未能加载文件或程序集“Interop.xxx 的问题

    1.关于在开发环境下COM组件的(来自 HRESULT 的异常:0x80080005 (CO_E_SERVER_EXEC_FAILURE)) 开发环境下,COM组件注册的文件 不一定是你自己现在程序调 ...

  5. 每天学五分钟 Liunx 111 | 存储篇:NFS

    NFS NFS(Network File System,网络文件系统),它可以通过网络让不同操作系统,不同机器共享彼此的文件. NFS 分为服务端和客户端.服务端提供共享的文件目录,客户端将该目录挂载 ...

  6. 你不知道的JavaScript APIs

    前言 在本文中,将介绍一些鲜为人知但却非常有用的API,如: Page Visibility API Web Share API Broadcast Channel API International ...

  7. 15-触摸按键控制LED灯

    1.触摸按键 触摸按键可分为四大类:电阻式,电容式,红外感应式和表面声波式 电阻式触摸按键使用人体破压电阻,改变电阻,实现开关效果,耐用性差,很少使用 红外感应式是通过红外扫描的方式,一般使用在比较恶 ...

  8. 在Vue中可以使用方括号法获得想要的对象数据吗?

    1.问题 Document {{message}} {{school.name}}{{school[mobile]}} 在这里 {{school.name}}{{school[mobile]}} 不可 ...

  9. pycharm等编辑器快捷键大赏

    1.序论 平时打代码的时候用鼠标在写代码的时候还行,代码一长就GG,快捷键便是我们的好选择 2.应用场景 1.问题一--选中一段指定区域 我想选中一段区域(我们一般用鼠标拖动),或者某一个标识符(光标 ...

  10. [转帖]GB18030 编码

    https://www.qqxiuzi.cn/zh/hanzi-gb18030-bianma.php GB18030编码采用单字节.双字节.四字节分段编码方案,具体码位见下文.GB18030向下兼容G ...