C/C++ 自制一个基于zlib的文件的(解)压缩系统

简介: C/C++ 自制一个基于zlib的文件的(解)压缩系统

项目展示





项目开发


编译设置


1. Debug/Release编译模式下的编译设置


首先,打开项目工程的属性页,然后将SDK和“平台工具集”选为“你当前的SDK和平台工具集”。


例如我是 SDK 10 平台工具集 v141。



接着,展开“C/C++”,点击“预处理器”,然后在“预处理器定义”中添加“ZLIB_WINAPI”,否则,代码不能编译过去。



接着点击“代码生成”,在“运行库”中设置为“/MTd”选项,表示Debug模式下的多线程静态编译。



接着,展开“链接器”,点击“命令行”,在“其他选项(D)”编辑框中添加链接命令“/FORCE:MULTIPLE” ,这个选项告诉链接器去创建一个有效的exe文件或dll文件,即使一个函数或变量被多次引用或多出定义。



这样,Debug模式下的编译设置就完成了。


设计思路


1. 数据或文件的压缩思路


对于数据或文件的压缩,主要是使用ZLIB库提供的 compress 压缩函数。


int compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);


compress函数将source缓冲区中的内容压缩到dest缓冲区。


sourceLen表示source缓冲区的大小(以字节计)。


destLen是传址调用,当调用函数时,destLen表示dest缓冲区大小(初始值不能为0);当函数退出后,destLen表示压缩后缓冲区的实际大小。


返回值:


-5 : 输出缓冲区不够大;


-4 : 没有足够的内存;


0 : 表示成功;


所以,对于文件的压缩,我们根据文件路径,打开文件并读取文件全部数据,然后调用compress函数进行压缩。


其中,需要注意的一个问题便是,对于上述的目的缓冲区的大小难以确定,不能笼统地认为直接使用压缩前文件大小作为压缩后的缓冲区大小。因为,有些小文件,压缩过后,数据可能反而会变大。所以,设计了一个循环,去处理这种情况:


do
{
  iRet = compress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
  if (0 == iRet)
  {
    // 成功
    break;
  }
  else if (-5 == iRet)
  {
    // 输出缓冲区不够大, 以 100KB 大小递增
    delete[]pDestData;
    pDestData = NULL;
    dwDestDataSize = dwDestDataSize + (100 * 1024);
    pDestData = new BYTE[dwDestDataSize];
    if (NULL == pDestData)
    {
      delete[]pSrcData;
      pSrcData = NULL;
      ::CloseHandle(hFile);
      return FALSE;
    }
  }
  else
  {
    // 没有足够的内存 或 其他情况
    delete[]pDestData;
    pDestData = NULL;
    delete[]pSrcData;
    pSrcData = NULL;
    ::CloseHandle(hFile);
    return FALSE;
  }
} while (TRUE);


我们获取compress函数的返回码,判断成功还是出错。


若出错,则判断返回的出错类型,若是返回码为 -5 ,则表示输出缓冲区不够大,这时,便重新申请更大的目的缓冲区,继续调用compress函数压缩数据,继续获取操作返回码。这样,便可解决目的缓冲区大小不明确的问题。


2. 数据或文件的解压缩思路


对于数据或文件的压缩,主要是使用ZLIB库提供的 uncompress 解压缩函数。


int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);


uncompress函数将source缓冲区中的内容压缩到dest缓冲区。


sourceLen表示source缓冲区的大小(以字节计)。


destLen是传址调用,当调用函数时,destLen表示dest缓冲区大小(初始值不能为0);当函数退出后,destLen表示压缩后缓冲区的实际大小。


返回值:


-5 : 输出缓冲区不够大;


-4 : 没有足够的内存;


0 : 表示成功;


所以,对于文件的解压缩,我们根据文件路径,打开文件并读取文件全部数据,然后调用uncompress函数进行解压缩。


针对输出缓冲区大小不明确的问题,我们解决思路也是按照上面数据压缩的解决思路来操作的,同样通过判断uncompress函数操作的返回码,来进行下一步操作。


编码实现


首先将zlib库相关文件放在源目录下



1. 导入ZLIB库文件


//*************************************************
//         zlib压缩库的头文件和静态库
//*************************************************
# include "zlib\\zconf.h"
# include "zlib\\zlib.h"
# ifdef _DEBUG
  #ifdef _WIN64
    #pragma comment(lib, "zlib\\x64\\debug\\zlibstat.lib")
  #else
    #pragma comment(lib, "zlib\\x86\\debug\\zlibstat.lib")
  #endif
# else
  #ifdef _WIN64
    #pragma comment(lib, "zlib\\x64\\release\\zlibstat.lib")
  #else
    #pragma comment(lib, "zlib\\x86\\release\\zlibstat.lib")
  #endif
# endif
//*************************************************


2. 文件压缩


// 数据压缩
// 输入:将要压缩文件的路径
// 输出:数据压缩后的压缩数据内容、数据压缩后的压缩数据内容长度
BOOL Zlib_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)
  {
    Zlib_ShowError("CreateFile");
    return FALSE;
  }
  // 获取文件大小
  DWORD dwFileSize = ::GetFileSize(hFile, NULL);       
  if (MAX_SRC_FILE_SIZE < dwFileSize)
  {
    ::CloseHandle(hFile);
    return FALSE;
  }
  // 判断是否满足大小限制条件
  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)
  {
    ::CloseHandle(hFile);
    return FALSE;
  }
  // 读取文件数据
  DWORD dwRet = 0;
  ::ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL);   
  if ((0 >= dwRet) ||
    (dwRet != dwFileSize))
  {
    delete[]pDestData;
    pDestData = NULL;
    delete[]pSrcData;
    pSrcData = NULL;
    ::CloseHandle(hFile);
    return FALSE;
  }
  // 压缩数据
  int iRet = 0;
  do
  {
    iRet = compress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
    if (0 == iRet)
    {
      // 成功
      break;
    }
    else if (-5 == iRet)
    {
      // 输出缓冲区不够大, 以 100KB 大小递增
      delete[]pDestData;
      pDestData = NULL;
      dwDestDataSize = dwDestDataSize + (100 * 1024);
      pDestData = new BYTE[dwDestDataSize];
      if (NULL == pDestData)
      {
        delete[]pSrcData;
        pSrcData = NULL;
        ::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;
  pSrcData = NULL;
  ::CloseHandle(hFile);
  return TRUE;
}


3. 文件解压缩


// 数据解压
// 输入:将要解压缩文件的路径
// 输出:数据解压后的数据内容、数据解压后的内容长度
BOOL Zlib_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)
  {
    Zlib_ShowError("CreateFile");
    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)
  {
    ::CloseHandle(hFile);
    return FALSE;
  }
  // 读取文件数据
  DWORD dwRet = 0;
  ::ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL);   
  if ((0 >= dwRet) ||
    (dwRet != dwFileSize))
  {
    delete[]pDestData;
    pDestData = NULL;
    delete[]pSrcData;
    pSrcData = NULL;
    ::CloseHandle(hFile);
    return FALSE;
  }
  // 解压缩数据
  int iRet = 0;
  do
  {
    iRet = uncompress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
    if (0 == iRet)
    {
      // 成功
      break;
    }
    else if (-5 == iRet)
    {
      // 输出缓冲区不够大, 以 100KB 大小递增
      delete[]pDestData;
      pDestData = NULL;
      dwDestDataSize = dwDestDataSize + (100 * 1024);
      pDestData = new BYTE[dwDestDataSize];
      if (NULL == pDestData)
      {
        delete[]pSrcData;
        pSrcData = NULL;
        ::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;
  pSrcData = NULL;
  ::CloseHandle(hFile);
  return TRUE;
}


4. 将数据保存为文件


// 将数据存储为文件
// 输入:数据原文件路径、将要保存的数据内容、将要保存的数据内容长度
BOOL SaveToOriginalFile(char *pszFileName, BYTE *pData, DWORD dwDataSize)
{
  char szSaveName[MAX_PATH] = { 0 };
  ::lstrcpy(szSaveName, pszFileName);
  ::PathStripPath(szSaveName);
  // 创建文件
  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)
  {
    Zlib_ShowError("CreateFile");
    return FALSE;
  }
  // 写入数据
  DWORD dwRet = 0;
  ::WriteFile(hFile, pData, dwDataSize, &dwRet, NULL);
  // 关闭句柄
  ::CloseHandle(hFile);
  return TRUE;
}


程序测试


我们在main函数中,调用上述封装好的函数,对文件进行压缩与解压缩的测试。main函数为:


int _tmain(int argc, _TCHAR* argv[])
{
  BOOL bRet = FALSE;
  BYTE *pCompressData = NULL;
  DWORD dwCompressDataSize = 0;
  BYTE *pUncompressData = NULL;
  DWORD dwUncompressDataSize = 0;
  // 压缩文件
  bRet = Zlib_CompressData("test.txt", &pCompressData, &dwCompressDataSize);
  if (FALSE == bRet)
  {
    return 1;
  }
  cout << "压缩成功!" << endl;
  // 保存压缩数据为文件
  bRet = SaveToOriginalFile("test.myzip", pCompressData, dwCompressDataSize);
  if (FALSE == bRet)
  {
    return 2;
  }
  cout << "保存压缩文件成功!" << endl;
  // 解压缩压缩文件
  bRet = Zlib_UncompressData("test.myzip", &pUncompressData, &dwUncompressDataSize);
  if (FALSE == bRet)
  {
    return 3;
  }
  cout << "解压缩成功!" << endl;
  // 保存压缩数据为文件
  bRet = SaveToOriginalFile("test_Uncompress.txt", pUncompressData, dwUncompressDataSize);
  if (FALSE == bRet)
  {
    return 4;
  }
  cout << "保存解压缩文件成功!" << endl;
  // 释放内存
  delete []pUncompressData;
  pUncompressData = NULL;
  delete []pCompressData;
  pCompressData = NULL;
  system("pause");
  return 0;
}


测试结果为:




可以看到,成功对大小为846KB的test.txt文件的数据进行压缩,得到大小为4KB的test.myzip文件。然后对test.myzip进行解压缩,得到和原来大小一样的test_Uncompress.txt文件。


项目链接


https://download.csdn.net/download/weixin_45525272/40317540


相关文章
|
11月前
|
C++ Windows
.NET Framework安装不成功,下载`NET Framework 3.5`文件,Microsoft Visual C++
.NET Framework常见问题及解决方案汇总,涵盖缺失组件、安装失败、错误代码等,提供多种修复方法,包括全能王DLL修复工具、微软官方运行库及命令行安装等,适用于Windows系统,解决应用程序无法运行问题。
1571 3
|
存储 监控 算法
员工屏幕监控系统之 C++ 图像差分算法
在现代企业管理中,员工屏幕监控系统至关重要。本文探讨了其中常用的图像差分算法,该算法通过比较相邻两帧图像的像素差异,检测屏幕内容变化,如应用程序切换等。文中提供了C++实现代码,并介绍了其在实时监控、异常行为检测和数据压缩等方面的应用,展示了其实现简单、效率高的特点。
569 15
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
Linux C++
Linux c/c++文件的基本操作
在Linux环境下使用C/C++进行文件的基本操作,包括文件的创建、写入、读取、关闭以及文件描述符的定位。
320 0
Linux c/c++文件的基本操作
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
561 0
|
Linux C++
Linux c/c++文件移动
这篇文章介绍了在Linux环境下,使用C/C++语言通过命令方式和文件操作方式实现文件移动的方法。
458 0
【状态压缩DP】 毕业旅行问题(C/C++)
【状态压缩DP】 毕业旅行问题(C/C++)
|
Linux API C++
超级好用的C++实用库之文件目录操作
超级好用的C++实用库之文件目录操作
331 0
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
548 12