65.9K
CodeProject 正在变化。 阅读更多。
Home

Zip Utils - 简洁、优雅、简单、C++/Win32

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (224投票s)

2004年6月26日

公共领域

8分钟阅读

viewsIcon

4530741

downloadIcon

45144

轻松添加zip/unzip功能,无需LIBS或DLL,具有优雅而强大的API

zipping and unzipping in action!

引言

警告:此代码存在已知错误。它未能正确处理非ASCII文件名。它未能正确处理密码。很遗憾,我没有时间修复它。但我已将其标记,以便其他金牌会员可以编辑它,以防有人想进行修复。

 

此源代码演示了如何为您的程序添加zip/unzip功能。许多人编写了自己的Zip包装器,事实上,CodeProject上也有几篇文章是基于我早期代码的。这个版本有什么不同?

  • 简洁的打包。 如果您需要zip功能,只需将zip.cppzip.h这对文件添加到您的项目中。如果您需要unzip功能,则需要unzip.cppunzip.h这对文件(如果您需要两者,则两者都添加!)。**无需**任何额外的库或DLL。
  • 简洁的API。 大多数其他zip/unzip API都非常糟糕。这个API是最好的。它简短、简洁,并且具有熟悉的Win32风格。大多数其他API将功能封装在类中,对于如此简单的问题来说,这是一种笨拙的过度设计,并且总是显得不够灵活。我的代码则不然。请参阅下面的代码片段。
  • 灵活性。 使用此代码,您可以从磁盘文件、内存缓冲区或管道中的zip文件中解压。您可以将文件解压到磁盘文件、内存缓冲区或管道。创建Zip文件也是如此。这意味着您不必再将文件写入临时目录后再使用它们!一个值得注意的特性是,您可以**直接从嵌入式资源解压**到内存缓冲区或磁盘文件,这对于安装程序来说非常有用。另一个特性是能够创建由系统页面文件支持的动态增长内存中的Zip文件。尽管功能强大,但API仍然保持简洁。这种强大并非来自仅仅包装别人的代码。它来自于重构zlib和info-zip源代码的内部结构。我的代码在这方面是独一无二的。
  • 加密。 此版本支持基于密码的Zip加密。许多其他Zip库,包括gzip,都不支持密码。
  • Unicode。 此版本支持Unicode文件名。
  • Windows CE。 此版本在Windows CE下可按原样工作。无需修改makefiles或#define,也无需担心任何LIB/DLL的兼容性。
  • Bug修复。 此代码基于gzip 1.1.4,该版本修复了1.1.3中的一个安全漏洞。(我早期代码的一个版本使用了1.1.3,并且已经渗入到其他CodeProject文章中……)。

在核心上,我的代码使用zlib和info-zip。有关致谢和许可,请参阅文章末尾。

Using the Code

要将zip功能添加到您的代码中,请将文件zip.cpp添加到您的项目中,并在源代码中`#include "zip.h"`。

同样,对于unzip功能,请将文件unzip.cpp添加到项目中,并在源代码中`#include "unzip.h"`。Zip和unzip可以很好地共存于同一个应用程序中。或者,如果您想节省空间,可以省略其中一个。

以下代码片段演示了如何使用zip/unzip。它们摘自下载中包含的演示应用程序之一。该应用程序还为Visual Studio .NET、Borland C++ Builder6和Embedded Visual C++ 3提供了项目文件。这里的代码片段使用ASCII。但是所有函数都接受`TCHAR*`类型的参数而不是`char*`,因此您可以在Unicode下正常使用它们。

示例 1 - 从现有文件创建Zip文件

  // We place the file "simple.bmp" inside, but inside
  // the zipfile it will actually be called "znsimple.bmp".
  // Similarly the textfile.

  HZIP hz = CreateZip("simple1.zip",0);
  ZipAdd(hz,"znsimple.bmp",  "simple.bmp");
  ZipAdd(hz,"znsimple.txt",  "simple.txt");
  CloseZip(hz);

示例 2 - 使用Zip文件内部名称解压Zip文件

  HZIP hz = OpenZip("\\simple1.zip",0);
  ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index;
  // -1 gives overall information about the zipfile
  for (int zi=0; zi<numitems; zi++)
  { ZIPENTRY ze; GetZipItem(hz,zi,&ze); // fetch individual details
    UnzipItem(hz, zi, ze.name);         // e.g. the item's name.
  }
  CloseZip(hz);

示例 3 - 直接从资源解压到内存

此技术对于小型游戏非常有用,您可以将所有资源打包到可执行文件中,同时限制文件大小。

假设我们使用*`.rc`*文件,其中包含*`1 RCDATA "file.zip"`*,将zip文件嵌入为资源。

  HRSRC hrsrc = FindResource(hInstance,MAKEINTRESOURCE(1),RT_RCDATA);
  HANDLE hglob = LoadResource(hInstance,hrsrc);
  void *zipbuf = LockResource(hglob);
  unsigned int ziplen = SizeofResource(hInstance,hrsrc);
  hz = OpenZip(zipbuf, ziplen, 0);
  ZIPENTRY ze; int i; FindZipItem(hz,"sample.jpg",true,&i,&ze);
  // that lets us search for an item by filename.
  // Now we unzip it to a membuffer.
  char *ibuf = new char[ze.unc_size];
  UnzipItem(hz,i, ibuf, ze.unc_size);
  ...
  delete[] ibuf;
  CloseZip(hz);
  // note: no need to free resources obtained through Find/Load/LockResource

示例 4 - 分块解压到内存缓冲区

通常,当您调用*`UnzipItem(...)`*时,它会返回*`ZR_OK`*。但如果提供的缓冲区太小,无法容纳全部内容,它将返回*`ZR_MORE`*。

  char buf[1024]; ZRESULT zr=ZR_MORE; unsigned long totsize=0;
  while (zr==ZR_MORE)
  { zr = UnzipItem(hz,i, buf,1024);
    unsigned long bufsize=1024; if (zr==ZR_OK) bufsize=ze.unc_size-totsize;
    ... maybe write the buffer to a disk file here
    totsize+=bufsize;
  }

常见问题

STRICT? 我认为您应该始终启用*`STRICT`*(在项目设置/预处理器/定义中),并开启所有警告。没有*`STRICT`*,*`HZIP`*句柄将与其他所有句柄混淆。

如何显示进度对话框? 包括的示例之一“progress”演示了如何实现这一点。

如何向现有Zip文件添加/删除文件? zip_utils目前只允许您打开*`OpenZip()`*进行解压,或*`CreateZip()`*进行添加,但不能混合使用。要修改现有的zip文件(例如,添加或删除文件),您需要创建一个新的zip文件,并将旧zip文件中的所有现有项目复制到新zip文件中。包含的示例之一“modify”演示了如何实现这一点。它定义了两个函数

  ZRESULT RemoveFileFromZip(const TCHAR *zip, const TCHAR *name);
  ZRESULT AddFileToZip(const TCHAR *zip, const TCHAR *name, const TCHAR *fn);
  // eg. AddFileToZip("c:\\archive.zip","znsimple.txt","c:\\docs\\file.txt");
  // If the zipfile already contained that thing (case-insensitive), it is removed.
  // These two functions are defined in "modify.cpp"

“fatal error C1010: while looking for precompiled header directive”(致命错误C1010:寻找预编译头指令时文件意外结束)。 要解决此问题,请选择*`zip.cpp`*和*`unzip.cpp`*,然后将*“Project > Settings > C++ > PrecompiledHeaders”(项目>设置>C++>预编译头)*更改为*“NotUsingPrecompiledHeaders”(不使用预编译头)*。

讨论

Efrat说:“我认为这个设计很糟糕”,因此当我说我的API很简洁而其他API不是时,他表示反对。(实际上,他说我的文档是我见过的最自大的,我的设计是我见过的最差的!)我在这里转载了他的评论以及我的回复,以便您能更明智地决定是否使用我的库。

  • [Efrat] 最好改用boost IOStream库

    [回复] 我很喜欢boost库。如果人们能够弄清楚如何将它添加到他们的项目中并用它进行zip/unzip,他们绝对应该使用boost而不是我的代码。(尽管我仍在尝试弄清楚它,并且在CE下无法编译它。)

  • [Efrat] 压缩存档有内部状态;它是一个经典的面向对象对象;作者对OOP的批评是不合理的。“OOP不意味着将代码放在CPP文件中。”

    [回复] 我试图避免面向对象。

    1. 您永远不会继承存档,也不会从它调用虚拟方法:我们只使用封装,而不使用OOP的其他支柱。通过使用不透明的句柄HZIP而不是类,我向程序员清楚地表明了这一点。另外,
    2. C++类在DLL之间无法干净地工作。像HZIP这样的句柄可以。
  • [Efrat] 例如,进度通知应该通过派生类中的虚拟函数来完成,而不是通过回调。

    [回复] 要获得进度,您可以在`while`循环中调用*`UnzipItem`*,每次迭代解压一部分文件。这很干净、可重入,并且具有简单的API。我认为这比继承类更容易。总的来说,我认为从库类继承是不好的。

  • [Efrat] 压缩应该放在DLL中。

    [回复] 我不同意。DLL对开发者和用户来说总是很麻烦。Unzip无论如何只增加40K。

  • [Efrat] API没有使用类型系统来区分用于zip的HZIP和用于unzip的HZIP。

    [回复] 这是故意的。zip和unzip之间的区别是当前实现上的一个缺点。我认为API应该是简洁的、“鼓舞人心的”,你不应该将当前的实现限制编码到类型系统中。

  • [Efrat] API使用错误代码,而不是异常,但任何从编程101毕业的人都知道异常更好。

    [回复] 我认为异常的普及程度远不如Efrat所建议的那样。此外,它们在DLL边界之间无法干净地工作,而且在Pocket PC上无法工作。

  • [Efrat] API不够灵活;它应该为更改而编码,而不仅仅是为设计时设想的所有选项(句柄、文件、内存)而编码。大多数用户会想到这个设计无法支持的源和目标。

    [回复] 原始的Zip使用*`FILE*`*,这实际上等同于Windows管道。我还提供了内存缓冲区,它们增加了巨大的灵活性,易于使用,并且无需额外的编程。对于任何*需要*通过内存缓冲区无法到达的源和目标的用户,他们不应该使用这些zip_utils。

  • [Efrat] 这不必要地特定于Windows。原始的zlib工作得很好且具有可移植性;zip_utils没有提供任何优势。压缩是内存操作和IO,因此不应平台特定。

    [回复] 在STL出现之前的旧时代,“跨平台”代码不可避免地意味着

    1. 充斥着如此多的*`#ifdef`*,以至于您无法阅读,
    2. 在Windows下无法直接工作。

    我从一个旧的代码库开始,因此Efrat提出的自底向上重写是不可能的。此代码相对于zlib的优点在于,它只需将单个文件添加到您的项目中,它在Windows下即可立即工作,您可以轻松地将其作为CPP模块添加到您的项目中(而不仅仅是dll/lib),并且API更简单。

总的来说,Efrat想要简洁可扩展的框架代码。我不是;我想要小巧紧凑的代码,这些代码本身就能很好地工作。此外,我认为“框架化”是行业中错误和代码超支的最大来源。

致谢

本文档的此版本已于2005年7月28日更新。非常感谢CodeProject的读者们发现了早期版本的错误并贡献了修复。有一个可怕的错误是,在解压了一个大文件后,下一个文件可能无法正常工作。Alvin77发现了这个错误。

我的工作是Jean-Loup Gailly和Mark Adler等人提供的www.gzip.org上的zlib代码的重打包形式。也来自www.info-zip.org上的info-zip源代码。再加上我自己的许多修改。原始源代码可以在两个提到的网站上找到。原始版权声明和许可证也可以在那里找到,并且也在我的代码的*`zip.cpp`*和*`unzip.cpp`*文件中。至于我自己的贡献的许可,我将其置于公共领域。

© . All rights reserved.