找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 10228|回复: 0

【C】zlib详细基础教程

[复制链接]
发表于 2014-4-2 01:38:31 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
zlib是一款用于压缩和解压缩的C语言库。它目前只支持zlib默认的压缩算法(deflation),并且文档里面说明了以后会提供更多的压缩算法,并保证调用接口差别不大。这个赞。说不定不久就会有7z算法压缩的PNG文件了……(我不了解PNG的详细格式所以不要喷我,但是我知道libpng用了zlib进行压缩)
要知道PNG文件内部是用zlib进行压缩的哦。由此可知zlib的用途是多么广泛了。详情请自己去了解libpng
zlib官网:http://www.zlib.net/
zlib把所有的文档资料写进了zlib.h这个头文件。我已经把这个文件的内容放到了论坛。
zlib.h头文件点此查看。

zlib使用起来相当简单,你只需要告诉它,你要压缩的数据在哪,有多大,你用来接收数据的地方在哪,容量有多大,它就会帮你完成压缩和解压缩的操作。你给它一个数据,它给你一个压缩过的数据。直观明了,简洁方便,真乃居家旅行必备库。(注:c4Droid内置了zlib库。因此在c4Droid你只需要#include"zlib.h"就能使用zlib,炒鸡方便啊)

zlib是按照线程安全的写法编写的,因此zlib也假设你的程序支持多线程,因此你需要给你的工程链接一个线程安全的C语言库。别忘了。

要使用zlib你需要了解一个结构体:z_stream这个结构体的定义写在了zlib.h头文件里,大家可以点此进去查看。

其中我们必须要了解的几个结构体成员如下:
  • next_in:输入缓冲区下一个字节的位置
  • avail_in:输入缓冲区剩下的字节数
  • total_in:目前从输入缓冲区处理了多少字节
  • next_out:输出缓冲区的下一个字节的位置
  • avail_out:输出缓冲区剩下的字节数
  • total_out:目前往输出缓冲区写入了多少字节

这么说感觉太绕口。那么我用另一个说法来解释一下。zlib是按照流水线的方式来对数据进行压缩解压的。你告诉它数据是从哪里输入的,然后你应该把数据输出到哪,它就会对数据进行操作比如压缩或解压缩。然后它读取了多少个数据,都会反映在这个结构体里面,以及它输出了多少数据,这些都是有反映的。

那么举个实际的例子:当我要压缩一个数组的时候,我应该怎么设置z_stream结构体的成员的值。代码如下:
  1. int 数组[0x233];
  2. int 压缩后的数组[0x233];//把压缩后的数组的容量设置成和原数组一样大,来保证能接收到全部的压缩过的数据
  3. z_stream z={0};
  4. z.next_in=数组;
  5. z.avail_in=sizeof(数组);
  6. z.next_out=压缩后的数组;
  7. z.avail_out=sizeof(压缩后的数组);
复制代码
好的,这样就算设置好了,接下来准备压缩。要压缩,首先你需要初始化压缩。我们调用deflateInit这个函数进行初始化。
函数原型如下:
int deflateInit(z_streamp strm,int level);
第一个参数当然是你设置好的z_stream的指针。第二个参数是“压缩级别”,意思是你希望zlib压缩得更快点还是压得“更用力”点。压缩级别的取值范围是0到9之间的整数,0表示完全不压缩,如果你设置压缩级别为0,那么zlib只是把源数据拷贝到目标缓冲区。而如果你设置压缩级别为9,那么zlib就会尽它的可能去压缩你的数据,能压多小压多小,当然也会导致它运行得相当慢。你可以根据需求把压缩级别设置为3、5、7等数值,选择更偏向于压缩速度或更偏向于压缩率。

调用完deflateInit后就算是初始化完成了,接下来就可以开始压缩了。当然接下来也请不要随意改变z_stream结构体的成员的值,免得出错。当然z.next_in、z.avail_in、z.next_out、z.avail_out这些是可以设置的,只要你不乱设置就不会出问题。

压缩的函数是deflate,你可以让它一次性完成所有的压缩,也可以给它一点数据,让它压缩,然后再给它一点数据,让它压缩,这样一步一步来完成压缩。
deflate函数的原型如下:
int deflate(z_streamp strm,int flush);
第一个参数当然是你设置好的z_stream的指针。第二个参数则是设置输出数据的方式,可选的数值有以下几个:
  • Z_NO_FLUSH:完全不同步。好处是能让zlib一次性读取所有的数据进行分析,这样来达到最大的压缩率。
  • Z_SYNC_FLUSH:所有要输出的数据都按照字节对齐输出到输出缓冲区,这样解压缩算法就能同时对齐进行解压缩,这个在网络传输数据的压缩上很有用。调用一次以后zlib会读取所有的数据进行分析。
  • Z_PARTIAL_FLUSH:所有要输出的数据都输出到输出缓冲区,但是并不会按照字节对齐。这保证解压缩算法在空固定码块(empty fixed code block)之前有足够空间来完成这个块(什么是空固定码块请不要问我,我也不知道)
  • Z_BLOCK:这个我们只需要知道,我们用不上,只有那些高级玩意儿比如WinRAR才需要考虑的东西。。。因为它需要考虑这些块的格式然后修改这些块来自己设置压缩的一些参数。。。
  • Z_FULL_FLUSH:这个会严重影响压缩率但是貌似具有一定的抗干扰性,大概。
  • Z_FINISH:完成压缩,把没写入的数据写入到输出缓冲区,通常在压缩最后一点数据的时候使用。


乍一看好像很复杂的样子,但是大概就是这样:
  • 如果你只是要一次性在内存中压缩一堆数据,你直接调用deflateBound(下面我会说这个函数)来取得压缩后的数据的大小,然后调用deflate(&你的z_stream,Z_FINISH)就可以压缩好了。
  • 如果你是断断续续地压缩一些数据但是追求最大的压缩比,你就先调用几次deflate(&你的z_stream,Z_NO_FLUSH)直到所有的数据都被读入,在最后调用deflate(&你的z_stream,Z_FINISH)完成所有的操作。
  • 如果你是做网络传输的程序,你可以选择Z_SYNC_FLUSH或Z_PARTIAL_FLUSH,如果你觉得你要追求逐字节精度,你就使用Z_SYNC_FLUSH,如果你觉得可以压缩得更高效率一点,你就使用Z_PARTIAL_FLUSH,大概。

别的都不用管了,反正我们需要的就是以上这些。
完成了所有的压缩操作之后,我们只需要调用deflateEnd(&你的z_stream)就可以了。这个函数把zlib内部申请的一些内存释放掉,结束压缩。这个时候我们的z_stream结构体就可以扔掉不要了。

返回值的说明:
deflateInit
  • Z_OK:成功。
  • Z_MEM_ERROR:内存不足。
  • Z_STREAM_ERROR:你说的压缩级别不在0到9之间的取值范围。
  • Z_VERSION_ERROR:版本不对,你调用zlib的方式和zlib库本身的版本不对应。

deflate
  • Z_OK:读取了所有的输入缓冲区的数据,压缩好了(但是并不意味着所有的压缩好了的数据都放到了输出缓冲区,因此Z_OK并不表示操作都完成了。)
  • Z_STREAM_END:所有的输入缓冲区的数据都读入了,所有的压缩的数据都写入到了输出缓冲区。意思是100%完成了操作。
  • Z_BUF_ERROR:你没有给它提供输入的或者输出的缓冲区。

deflateEnd
  • Z_OK:成功了。
  • Z_STREAM_ERROR:流的状态不对。
  • Z_DATA_ERROR:丢失数据了。

解压缩的调用方式也差不多,只不过inflateInit的原型是
int inflateInit(z_streamp strm);
因为解压缩不存在压缩比这个问题。
inflate的flush参数可以是Z_NO_FLUSH、Z_SYNC_FLUSH、Z_FINISH、Z_BLOCK、Z_TREES。
  • Z_NO_FLUSH:读取压缩的数据进行分析,但是不放到输出缓冲区。
  • Z_SYNC_FLUSH:读取压缩的数据进行分析,你的输出缓冲区有多大,就放多少到你的输出缓冲区。
  • Z_FINISH:一次性读取压缩的数据进行分析然后输出
  • Z_BLOCK:不知道
  • Z_TREES:不知道

本帖被以下淘专辑推荐:

回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-23 16:16 , Processed in 0.033824 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表