心得 ~ 使用 zlib库 解压缩 zip文件

最近在完成一个项目,需要用到C++语言读取一个zip文件内指定文件的内容。在网上查阅了不少资料,针对过程中遇到的问题,自己也研究了一下,现将方法心得记录下来。

关于解压文件的方法,根据网上的资料,大概有以下三种方法:

  1. 调用rar.exe等外部程序。
  2. 使用第三方类库。
  3. 自己写解压方法。

第一种方法,个人感觉不太靠谱,舍去。第三种方法,本人对zip压缩算法一头雾水,加上暂时没必要做此类研究,舍去。直接拿来主义,用第二种。

第三方类库选用有名的zlib,官方网址为http://www.zlib.net/,我是从http://www.winimage.com/zLibDll/index.html下载的,1.2.5版。

在此需要说明一点,如果你用的是Visual Studio,只需要下载zlib源码即可,完了需要自己运行生成动态库文件;如果不想太麻烦,或者只有VC++6.0,需要将动态库文件也下载下来。我机子上只有VC++6.0,因此直接把源码和动态库文件都下载了,如果你想自己编译生成,可以参考http://blog.sina.com.cn/s/blog_659b2b3201013y9k.html

下载完成后,就可以使用zlib库了。

 

zlib库使用前的配置

zlib库在使用的时候,不是简单地直接include就可以了,还需要一些配置。

由于我只是使用简单的解压功能,所以只需要以下文件即可:

zlib-1.2.5\zlib.h

zlib-1.2.5\zconf.h

zlib-1.2.5\contrib\minizip\unzip.h

zlib-1.2.5\contrib\minizip\unzip.c

zlib-1.2.5\contrib\minizip\ioapi.h

zlib-1.2.5\contrib\minizip\ioapi.c

我在工程目录中建了个zlib文件夹,将这些文件拷贝进去。如果你在编译的过程中遇到缺少头文件的错误,根据错误信息将对应的zlib库文件加入即可。

接下来,将动态库文件也拷贝进来,也就是zlibwapi.lib文件和zlibwapi.dll文件。

接着在需要用到zlib的地方,加入以下代码即可:

#define ZLIB_WINAPI
#include "zlib/unzip.h"
#pragma comment(lib, "zlib/zlibwapi.lib") 

注意 ,预处理定义和动态库是必须的,否则link的时候会出错。我这里也是研究了半天才搞定,不知道大家有没有遇到这种问题。

在这里还有另一种方法,加载预处理和动态库。以VC++6.0为例。

  1. 打开 project -> settings,选择 C/C++ -> General,在 Preprocessor definitions 中,加入 ZLIB_WINAPI;
  2. 接着选择 Link -> Input,在 Object/library modules 和 Additional library path 中加入 zlib/zlibwapi.lib;
  3. 保存。

这样在使用的时候,只需要include头文件即可。

此外,如果你在运行程序的时候遇到以下错误:

无法启动此程序,因为计算机中丢失 zlibwapi.dll。尝试重新安装该程序以解决此问题。

只需要将 zlibwapi.dll 文件,拷贝到系统目录下的 Windows\system 目录下即可。

 

使用zlib解压文件

对于zlib的函数定义,具体使用方法等,网上资料很多。zlib的源码中也提供了使用范例,有兴趣的朋友可以研究一下。我这里只介绍自己用到的相关方法。

 

以下是打开zip文件的方法:

// zip文件路径
char FILEPATH[] = "aa.zip";

unzFile zFile;
zFile = unzOpen64(FILEPATH);
if (zFile == NULL)
{
	cout << FILEPATH << "文件打开失败" << endl;
	return -1;
}

如果 zFile 不等于空,就表示文件打开成功,可以继续接下来的工作。

 

首先来获取压缩文件的全局信息:

unz_global_info64 zGlobalInfo;

if (UNZ_OK != unzGetGlobalInfo64(zFile, &zGlobalInfo))
{
	// 错误处理
	cout << __FILE__ << "中" << __LINE__ << "行错误;得到全局信息出错!" << endl;
	return -1;
}

unz_global_info64 是zlib库中定义的结构体,里边最重要的成员变量就是压缩文件内所有文件的数量。注意这个文件的数量并不包括目录。

 

下面循环遍历所有文件:

unz_file_info64 zFileInfo;
unsigned int num = 512;
char *fileName = new char[num];

for (int i = 0; i < zGlobalInfo.number_entry; i++)
{
	// 遍历所有文件
	if (UNZ_OK != unzGetCurrentFileInfo64(zFile, &zFileInfo, fileName, num, NULL, 0, NULL, 0))
	{
		//错误处理信息
		cout << __FILE__ << "中" << __LINE__ << "行错误;得到当前文件信息出错!" << endl ;
	}
	unzGoToNextFile(zFile);
}

 

extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file,
                         unz_file_info64 *pfile_info,
                         char *szFileName,
                         uLong fileNameBufferSize,
                         void *extraField,
                         uLong extraFieldBufferSize,
                         char *szComment,
                         uLong commentBufferSize));

该函数的功能,是获取压缩包内当前读取的文件信息。其中比较重要的作用是获取当前读取的文件名,通过 szFileName 参数返回,这是一个char类型的数组,而 fileNameBufferSize 的值,就是返回的文件名的长度。也就是说,如果当前文件名是 "abcdefg.txt",而 fileNameBufferSize 的值设为了4,则最后 szFileName 的值就是 "abcd"。还有一点需要说明的是,这里返回的文件名,是包括目录路径在内的全路径。

unz_file_info64 也是zlib库中定义的结构体,保存的是当前文件的信息,比较常用的成员变量有:

uLong compression_method            压缩方法

uLong dosDate                                     最后修改日期

ZPOS64_T compressed_size           压缩后的文件大小,按字节数计

ZPOS64_T uncompressed_size      原始文件大小,解压后的文件大小,按字节数计

uLong size_filename                          文件名长度

获取了当前文件的信息,接下来就可以解压获取文件内容了:

if (UNZ_OK != unzOpenCurrentFile(zFile))
{
	//错误处理信息
	cout << __FILE__ << "中" << __LINE__ << "行错误;打开压缩包中" << fileName << "文件失败!" << endl ;
}
cout << "压缩文件" << fileName << "内容为:" << endl ;
int fileLength = zFileInfo.uncompressed_size;
char *fileData = new char[fileLength];

int len = 1 ;
while (len)
{
	//解压缩文件
	len = unzReadCurrentFile(zFile, (voidp)fileData, fileLength - 1);
	fileData[len] = '\0';
	for (int j = 0; j < len; j++)
	{
		file.push_back(fileData[j]);
	}
}
for (int j = 0; j < file.size(); j++)
{
	cout << file[j];
}
unzCloseCurrentFile(zFile);
free(fileData);

首先通过函数 unzOpenCurrentFile 打开当前文件,然后通过函数 unzReadCurrentFile 读取当前文件信息,相关处理完成后需要关闭当前文件。

extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
                      voidp buf,
                      unsigned len));

该函数的意思,是按照 len 的长度,按字节读取当前文件的内容,保存到 buf 中,并返回实际读到的字节数。也就是说,如果当前文件大小为95个字节,给定 len = 10,则每次只会读取10个字节的数据保存到 buf 中,每次调用函数返回10。而最后一次读取,虽然也是读取10个字节的数据,但文件只剩下5个字节数据还未读到,那么最终的返回结果就成了5。

因此在实际读取文件的时候,可以按照当前文件信息内的 zFileInfo.uncompressed_size 变量的值,一次性读取全部文件内容;或者按照不同需求,读取不同长度的内容。

如果在当前zip文件中,只需要某一个文件的内容,而又知道具体文件名的话,就可以不通过循环,直接定位到所需文件。

char *fileName = "aa.txt";
if (UNZ_OK == unzLocateFile(zFile, fileName, 0))
{
	// 文件处理
}

需要注意的是,此处传入的文件名,必须是包括目录路径在内的全路径,否则搜索不到。

extern int ZEXPORT unzLocateFile OF((unzFile file,
                     const char *szFileName,
                     int iCaseSensitivity));

该函数中 iCaseSensitivity 参数表示文件匹配方式,1表示区分大小写,2表示不区分大小写,0表示按照操作系统确定,linux下区分,windows下不区分。

 

最后,需要调用

unzClose(zFile)

关闭打开的zip文件。同时不要忘了释放相关变量的内存。

 

后记

这篇文章只是记录了自己项目中用到的zlib使用方法,并没有做更深入的研究。而且由于项目需求原因,我暂时没有研究如何通过zlib库压缩文件。如果朋友们对这方面有兴趣,可以在网上搜索相关资料,自己研究下,还是比较简单的。

如果文章中有什么错误的地方,欢迎大家指正。

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值