什么是压缩
- 数据压缩并不只是对数据的处理,它还是找出数据中已有结构的过程
- 在数据压缩中,首先要为各种数据中的不同结构建立模型,然后利用这些模型(可能还需要最终感知这些数据的一些应用环境),以更紧凑的方式来表达这些数据。
- 这些结构可能是非常简单的图案,只要画出数据的图形就能看到;也可能比较复杂,需要更为抽象的方法才能体会到
- 信息常常包含在数据的结构中,而不是包含在数据本身中,数据压缩技术的发现与这些结构的发现分不开
要研究数据压缩,必须先了解衡量压缩性能的指标:压缩率
- 要衡量一种压缩算法对给定数据集进行压缩时的表现,一种很合理的方法是看一下在压缩前后,数据表示所需比特数之比,这个比较叫做压缩比
- 假定有一副图像由256像素×256像素的方形阵列组成,存储它需要65536字节。对该图像进行压缩,压缩后的版本需要16384字节。我们就说压缩比为4:1
- 压缩比还有一种表示方式:将所需数据的减少量表示为原数据大小的百分比。具体到上面这个例子,以这种方式计算得出的压缩比为75%。”
gzip文件格式
gzip不是一种算法,可以说它是一种压缩工具,或者说它是一种文件格式。
我更倾向于将gzip作为一种文件格式来看待,因为不管是用什么软件去压,也不管用哪种实现库去压,只要最终结果是gzip的压缩结构,那么该结果肯定是按照gzip文件格式组织的
可以把gzip文件格式理解为一只虾(头、中间、尾),包括三个部分(文件头、文件尾、中间保存被压缩后的数据)
注:gzip压缩不支持加密
gzip文件头
文件头由固定长度的部分和扩展部分组成, 扩展部分不一定存在,尤其是网络传输中使用的HTTP格式压缩,如果使用了gzip格式,那么对应的压缩报文一般都不带扩展部分。gzip文件格式通过将头部中定长部分的某些比特位置来标识头部是否带有扩展部分,我们一一来看。
文件以10字节的定长部分开始
上面两个“+”之间的内容代表一个字节,所以上面除了MTIME使用四个字节之外,其他只占用一个字节
- ID1和ID2(IDentification):这两个字节用于标识gzip文件,其中,ID1 = 31(0x1f,\037),ID2 = 139(0x8b,\213),如果判断某文件以这两个字节开头,那么可以初步认为这是gzip文件,但具体是不是,必须该文件格式完全符合gzip文件格式才行
- CM(CompressionMethod):这个字段用于表示当前gzip压缩文件内部的压缩结构所使用的压缩方法,取值范围[0,8]。其中[0, 7]保留,目前只用8,表示gzip使用deflate压缩方法
- FLG(FLaGs):标记位,该标记位中的每一比特分别代表后面对应扩展位是否存在,各比特位含义如下,
- Bit 0 FTEXT:
- 如果置位,则表明文件(应该是指被压缩文件)是ASCII文本文件。
- 这一位是否置位是可选的,压缩工具通过检查少量输入数据中是否含有非ASCII字符来将其置位。
- 一旦怀疑有非ASCII字符,就不置位,表示这是二进制文件。
- 那些对ASCII文本文件和二进制文件使用不同文件格式的系统,解压缩工具会通过这一位是否置位来决定合适的文件格式。
- 我们故意不让压缩所使用的算法来设置这一位,因为压缩工具本身可以选择是否将其置位,而且解压缩工具通常也可以选择忽略这一位并将数据转换的问题抛给其他程序。
- Bit 1 FHCRC
- 如果置位,表示会对gzip文件头部进行CRC16校验,校验结果会放在实际的压缩数据之前,紧挨着实际的压缩数据。
- CRC16由CRC32的两个低有效字节组成,CRC32用于计算整个gzip头的校验和,但计算时不包括CRC16所占的这两个字节。
- 这一位一般不会被置上。(注意:这里说的这个CRC32与我们后面要说的位于gzip文件尾的那个CRC32不是同一个CRC32!!!但都是用32位循环冗余校验码算法计算得到的,只是作用对象不同。这里的CRC32作用对象是gzip头,完成计算后只取两个低有效字节,构成CRC16;而我们后面要说的位于gzip文件尾的CRC32的作用对象是全部的原始待压缩数据,这两个概念一定要理清)。
- Bit 2 FEXTRA:如果置位,表示带着扩展gzip头部部分。扩展部分后续介绍。
- Bit 3 FNAME
- 如果置位,表示携带被压缩文件的文件名(没被压缩),该文件名会以’\0’结束(就是个字符串)。
- 该文件名必须由ISO 8859-1 (LATIN-1) 中的字符组成;在那些使用EBCDIC或其他字符集组成名称的系统中,文件名称必须被转换为ISO LATIN-1 字符集才能让gzip文件携带。这个文件名是被压缩文件的原始文件名,不携带任何的路径信息,只是个文件名而已。
- 如果被压缩文件在一个对名称字母大小写不敏感的文件系统上,则文件名称必须全部小写。
- 如果被压缩数据不是来自一个有文件名称的文件,则不携带文件名称(比如使用gzip压缩HTTP应答报文);例如,被压缩数据来自Unix系统的标准输入,则gzip文件不携带文件名。
- Bit 4 FCOMMENT
- 如果置位,表示携带以‘\0’结尾的文件说明(这个文件说明也是个字符串)。这个说明只是给人们去使用的,类似HTTP应答报文头部中的状态码原因短语。
- 这个文件说明也必须使用ISO 8859-1 (LATIN-1)中的字符。换行时应该用一个十进制换行符。
- Bit 5~7 预留,必须全0
- MTIME(ModificationTIME):
- 该字段给出了那个被压缩的原始文件最近被修改的时间。
- 该时间使用Unix格式,即,自1970年1月1日0时起到现在的秒数。
- 注意,对于MS-DOS或其他使用本地时间而不是使用通用时间的系统,这种方式可能会引起问题。如果不是压缩一个文件,那么该字段就是压缩工作开始的那个时间。
- 如果该字段为0,则表示没有可用的时间戳(这种情况在使用gzip的HTTP压缩报文中很常见)。
- XFL= 2 – 压缩率最大但是压缩速度最慢(的那个压缩级别);
- XFL= 4 – 最快的压缩(级别);
- (注:deflate 是分0~9种压缩级别的)
- OS(OperatingSystem)
- 0- FAT filesystem (MS-DOS, OS/2, NT/Win32)
- 1- Amiga
- 2- VMS (or OpenVMS)
- 3- Unix
- 4- VM/CMS
- 5- Atari TOS
- 6- HPFS filesystem (OS/2, NT)
- 7- Macintosh
- 8- Z-System
- 9- CP/M
- 10- TOPS-20
- 11- NTFS filesystem (NT)
- 12- QDOS
- 13- Acorn RISCOS
- 255– unknown
头部扩展字段
上面10个字节是无论如何都会存在的,而这里所描述的扩展字段,就是根据上面那10个字节来决定是否存在的。共分为四个部分,
按照顺序依次是: FEXTRA+ FNAME + FCOMMENT+ FHCRC,
不一定都会存在,但是只要存在,不论存在几个,一定要按照顺序来,例如,FHCRC 和FNAME都存在,那么FNAME 一定要在FHCRC前面!!!下面我们逐个分析。
FEXTRA:
XLEN用两个字节记录,表示extra field部分的大小。而extra field部分又细分为如下结构,
(我将这个部分翻译为次级域)SI1和SI2为这个次级域提供一个ID,这个ID通常由两个便于记忆的ASCII字母表示(这句话不知道是不是应该这么翻译)。Jean-Loup Gaillygzip@prep.ai.mit.edu(gzip源码作者)维护了一张次级域表,你可以将自己的次级域ID发送给他。SI2 = 0 的次级域ID目前保留,未来再用。现在的次级域ID是这样定义的,
LEN给出了次级域数据部分的长度,但是不包括SI1、SI2和LEN这四个字节。 FNAME:
以‘\0’结束,就是个字符串 FCOMMENT:
以‘\0’结束,就是个字符串 FHCRC:
这个后面就是实际的压缩数据了,也就是虾的身子部分
文件尾
相比gzip文件头,gzip文件尾较简单,只由四个字节组成
- CRC32(Cyclic Redundancy Check):用标准循环冗余校验算法对原始数据进行计算的结果
- **ISIZE(InputSIZE) **:将原始数据大小对
2^32
取模的结果(因为只能用四个字节存结果,所以只能对2^32
取模)
文件体
上面已经将这个gzip文件的基本文件格式分析完毕,这里简单说下gzip文件的文件体。该文件体本身和gzip没有多大关系了,因为只要用到defalte的文件格式,这部分都是一样的,比如gzip的文件体和[Kzip的文件体“基本”是一样的,因为它们都使用了defalte。换句话说,这部分的格式就全部由defalte算法(或者格式)决定了
gzip文件格式实例分析