分享:自己写的压缩解压缩程序,某些情况下可比winRar压缩比更高

liangbch 2012-05-09 04:10:03
对于哈夫曼编码,在学习数据结构时就知道,但我从没有写过一个程序,前2天,突然想搞清楚哈夫曼编码的原理,遂写了一个程序,用来压缩很难构造字典的,包含大量数字的文本文件,测试结果发现,这种方法的压缩效果高于winzip,但低于winRAR. 随后,进一步改进,支持2种压缩模式,第2种模式比第一种模式更复杂,但压缩效果更好。这一次,终于超过WinRAR,算是一点儿成就吧,这里和大家分享一下算法。
完整的源代码包,包括用法,测试结果,编译后的程序,测试数据已经上传到csdn. 不久将给出链接。

这里亦可讨论各种压缩算法,欢迎大家给出建议。
...全文
372 点赞 收藏 16
写回复
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
aCracker 2012-05-18
好厉害,收藏起来.
回复
liangbch 2012-05-10
容易知道,windows格式的文本文件,每行总是以0x0d,0x0a结束,但在上表中,这两个字符
的huffman编码长度不同。我们知道,8位2进制数的编码空间为256,但这个文件只用了其中
的57个字符。是否可以将2个字符组合重新定义一个没有用过的编码呢?是。我的程序中的
第2种编码方法(对应参数-m2)就是这么做的。这个方法基本过程是:

1.找出文中的2字符组合(我的处理方法是,仅仅查找字母和字母,数字和数字,符号和符
号的组合)中出现频度最高的一部分,这部分组合应该少于编码空间中未使用的字符。
2.为2字节组合重新定义一个新的单字节编码。
3.将2字节组合替换为1个字符,字符的个数大约变为原先的60%左右。
4.对变换后的内容,重新统计每个单字节编码的出现频度。
5.并对每个单字节编码做huffman编码。

从下表看出,对于这个文件,旧的字符和对双字节字符给出的新的单字节编码定义总数为197,
依然没有超过8bit二进制的编码空间256。不过这种方法需要一个转化表,在编码时,需要将
2字节字母组合转化为单字节,在解码时,需要将单字节码转化为1个字符或者2个字符。因此,
压缩文件中需要包含一个转化表,179个编码需要179×2个字节的转化表。

下表是PIfast100万位圆周率文件中,各个字符和双字节字符组合的出现次数和huffman编码。
-------------------------------------------------------------------------
单字符或者双字符 出现次数 huffman编码
[0d 0a] 22018 11001
[' '] 20001 00100
[' ('] 2 1111001000001011
[' :'] 11 11110010011101
[' ='] 1 11110010010110010
[' ~'] 2 1111001001000110
['),'] 1 11110010010110011
['--'] 30 111100100001
['00'] 14338 111101
['01'] 5154 0010101
['02'] 5179 0010110
['03'] 5282 0011110
['04'] 5180 0010111
['05'] 5239 0011000
['06'] 5072 11110011
['07'] 5139 0010100
['08'] 5283 0011111
['09'] 5264 0011010
['10'] 5449 1010111
['11'] 5457 1011011
['12'] 5415 1000111
['13'] 5506 1101001
['14'] 5406 1000011
['15'] 5433 1010001
['16'] 5287 0100000
['17'] 5443 1010100
['18'] 5523 1101100
['19'] 5488 1100010
['20'] 5359 0110000
[' '] 80050 000
['21'] 5423 1001010
['22'] 5420 1001000
['23'] 5303 0100101
['24'] 5393 1000000
['25'] 5543 1110001
['26'] 5422 1001001
['27'] 5550 1110011
[')'] 1 11110010010110100
['28'] 5398 1000010
['29'] 5326 0101000
['.'] 6 111100100111101
['30'] 5564 1110100
['0'] 1823 111100101
['1'] 3 1111001001111001
['3'] 2 1111001001000111
['4'] 1 11110010010110101
['8'] 1 11110010010110110
[':'] 4 111100100100000
['31'] 5379 0111010
['32'] 5489 1100011
['33'] 5461 1011101
['34'] 5270 0011011
['35'] 5549 1110010
['36'] 5312 0100110
['37'] 5463 1011110
['38'] 5371 0110111
['39'] 5485 1100001
['40'] 5410 1000100
['K'] 2 1111001001001000
['41'] 5370 0110101
['42'] 5370 0110110
['43'] 5355 0101100
['44'] 5378 0111001
['45'] 5532 1101111
['T'] 1 11110010010110111
['46'] 5444 1010101
['47'] 5423 1001011
['48'] 5379 0111011
['49'] 5459 1011100
['50'] 14617 111110
['51'] 5291 0100011
['52'] 5426 1001101
['53'] 5435 1010010
['54'] 5494 1101000
['55'] 5613 1110111
['d'] 3 1111001001111100
['g'] 1 11110010010111000
['56'] 5428 1001110
['l'] 1 11110010010111001
['m'] 1 11110010010111010
['n'] 7 11110010000011
['57'] 5465 1011111
['s'] 3 1111001001111101
['t'] 1 11110010010111011
['x'] 1 11110010010111100
['y'] 2 1111001001001001
['58'] 5302 0100100
['59'] 5425 1001100
['60'] 5614 1111000
['61'] 5278 0011101
['62'] 5453 1011001
['63'] 5356 0101110
['64'] 5364 0110010
['65'] 5527 1101101
['66'] 5287 0100001
['67'] 5441 1010011
['68'] 5355 0101101
['69'] 5320 0100111
['70'] 5477 1100000
['71'] 5273 0011100
['72'] 5372 0111000
['73'] 5387 0111110
['74'] 5567 1110101
['75'] 5385 0111101
['76'] 5515 1101011
['77'] 5290 0100010
['78'] 5336 0101010
['79'] 5249 0011001
['80'] 5536 1110000
['81'] 5381 0111100
['82'] 5360 0110001
['83'] 5412 1000101
['84'] 5451 1011000
['85'] 5512 1101010
['86'] 5454 1011010
['87'] 5369 0110100
['88'] 5390 0111111
['89'] 5428 1001111
['90'] 5448 1010110
['91'] 5353 0101011
['92'] 5412 1000110
['93'] 5393 1000001
['94'] 5609 1110110
['95'] 5530 1101110
['96'] 5335 0101001
['97'] 5368 0110011
['98'] 5356 0101111
['99'] 5431 1010000
[': '] 20000 111111
['=='] 60 11110010001
['Ch'] 1 11110010010111101
['Co'] 2 1111001001001010
['Di'] 1 11110010010111110
['Du'] 1 11110010010111111
['En'] 1 11110010011000000
['FF'] 1 11110010011000001
['Fa'] 1 11110010011000010
['Go'] 1 11110010011000011
['Ma'] 2 1111001001001011
['Me'] 2 1111001001001100
['Ph'] 1 11110010011000100
['Pi'] 4 111100100100001
['Pr'] 1 11110010011000101
['Si'] 1 11110010011000110
['St'] 1 11110010011000111
['To'] 1 11110010011001000
['We'] 2 1111001001001101
['Xa'] 1 11110010011001001
['al'] 1 11110010011001010
['ar'] 1 11110010011001011
['at'] 4 111100100100010
['by'] 1 11110010011001100
['co'] 3 1111001001111110
['di'] 2 1111001001001110
['do'] 1 11110010011001101
['ed'] 3 1111001001111111
['er'] 1 11110010011001110
['fi'] 1 11110010011001111
['fo'] 1 11110010011010000
['gi'] 2 1111001001001111
['ho'] 1 11110010011010001
['ic'] 1 11110010011010010
['in'] 1 11110010011010011
['io'] 5 111100100111001
['ky'] 1 11110010011010100
['me'] 3 111100100000000
['mo'] 2 1111001001010000
['mp'] 3 111100100000001
['nd'] 2 1111001001010001
['no'] 1 11110010011010101
['od'] 1 11110010011010110
['of'] 3 111100100000010
['og'] 1 11110010011010111
['on'] 1 11110010011011000
['ra'] 2 1111001001010010
['rm'] 1 11110010011011001
['rs'] 1 11110010011011010
['ru'] 1 11110010011011011
['ry'] 2 1111001001010011
['se'] 2 1111001001010100
['sk'] 1 11110010011011100
['st'] 1 11110010011011101
['ta'] 1 11110010011011110
['th'] 2 1111001001010101
['ti'] 2 1111001001010110
['ts'] 2 1111001001010111
['ud'] 1 11110010011011111
['ur'] 2 1111001001011000
['us'] 3 111100100000011
['ut'] 3 111100100000100
['ve'] 1 11110010011100000
['vi'] 1 11110010011100001
['vs'] 1 11110010011100010
['wi'] 1 11110010011100011
['ys'] 1 11110010011110000
['ze'] 1 11110010011110001
['~ '] 1 1111001000001010
回复
liangbch 2012-05-10
现在我们以Pifast100万位圆周率为例,说说我的两种编码方法

下表是piFast各个字符的出现次数和我程序中给出的huffman编码,可以看到,总共出现了57个不同的字符。
-------------------------------------------------------------------------------------
字符 出现次数 huffman编码
[0a] 22018 100011
[0d] 22018 10000
[' '] 140069 011
['('] 2 100010011110010
[')'] 2 100010011110011
[','] 1 100010000000110
['-'] 60 1000100110
['.'] 6 1000100010100
['0'] 135765 010
['1'] 107769 1010
['2'] 108033 1101
['3'] 108235 000
['4'] 108233 1111
['5'] 118359 001
['6'] 107551 1001
['7'] 107802 1011
['8'] 107987 1100
['9'] 108110 1110
[':'] 20015 1000101
['='] 121 100010010
['C'] 3 10001000001111
['D'] 2 100010011110100
['E'] 1 100010000000111
['F'] 3 10001000101100
['G'] 1 100010000001000
['K'] 2 100010011110101
['M'] 4 10001001110110
['P'] 6 1000100010101
['S'] 2 100010011110110
['T'] 2 100010011110111
['W'] 2 10001000000000
['X'] 1 100010000001001
['a'] 13 100010001100
['b'] 1 100010000001010
['c'] 4 10001001110111
['d'] 13 100010001101
['e'] 15 100010001111
['f'] 5 1000100000011
['g'] 4 10001001111000
['h'] 5 1000100000110
['i'] 22 10001000010
['k'] 2 10001000000001
['l'] 2 10001000000010
['m'] 10 100010000010
['n'] 13 100010001110
['o'] 24 10001000100
['p'] 3 10001000101101
['r'] 12 100010000111
['s'] 15 100010011100
['t'] 17 100010011111
['u'] 11 100010000110
['v'] 3 10001000101110
['w'] 1 100010000001011
['x'] 1 100010000011100
['y'] 7 1000100111010
['z'] 1 100010000011101
['~'] 3 10001000101111
---------------------------------------------------
回复
千树之影 2012-05-10
你在设计压缩算法时是假设已知要压缩的文件是只含数字的文本文件了吗?
回复
liangbch 2012-05-10
我们考虑圆周率的2进制形式存储,用30比特来表示9位10进制数,这种方式完全丢弃了格式。即使这样,每个10进制字符依然需要30/9=3.33比特 (2^30=1073741824,刚好可以表示9位10进制数)。可以看到huffman编码和直接使用2进制存储相差无几了。

下面我们看看目前最强的使用算术编码技术的压缩软件,其压缩比如何,我使用PeaZip 4.5,压缩格式选择PAQ80,对supper-pi产生的包括小数点后1048576数字的文件进行压缩,其结果令人吃惊。1176511字节的源文件压缩后仅为436597字节,按此推算,原文件的1个字节,压缩后仅仅2.969字节,比2进制格式表示圆周率的还要小。不过这个软件的压缩速度可以用恐怖来形容,用我现在正在使用的电脑(2005年买的CPU为迅驰1.7G的笔记本电脑)对这个仅为1.1M字节的文件进行压缩,耗时竟然达到7分钟左右。
---------------------------------------------------------------------------------------------------------------------

下面给出我的这个程序和winRar,winzip的压缩率对比数据,被压缩的文件是super-pi和piFast生成的100万位圆周率。从下表可以看出,对于super-pi文件,我的程序的第一种模式,其压缩优于zip而低于rar,第2种模式,其压缩比超过了rar和zip。对于pi-fast文件,其开头部分的文件比较正常,这时字典算法起作用了,而我的程序并没有字典算法,仅仅使用huffman编码,故压缩比稍稍低于RAR,(42.35 vs 42.14)。

压缩工具 命令参数 源文件 源文件大小 压缩后大小 压缩比(压缩文件大小除以原文件大小)
Huffzip -m1 A 1,176,511 544,046 46.24%
Huffzip -m2 A 499,736 42.47%
winRAR 最大压缩 A 526,983 44.79%
winZip 最大压缩 A 574,376 48.82%

Huffzip -m1 B 1,322,392 614,729 46.48%
Huffzip -m2 B 560,137 42.35%
winRAR 最大压缩 B 557,287 42.14%
winZip 最大压缩 B 616,083 46.58%

A:supper-pi 1048576位圆周率文件
B:PiFast 100万位圆周率文件
回复
liangbch 2012-05-10
我的程序使用的被压缩的的数据文件为super-pi和pifast生成的100万位圆周率的文本文件,长度较长的重复的字符串(字符组合)很少,基于LZ77,LZ78,LZW的字典算法对此文件几乎无效,这也是我的huffman编码能够大别WINRAR的原因之一。下面是这2个文件的前几十行。

supper PI的文件内容,前43行
------------------------------------------------------------------------
PI=3.

1415926535 8979323846 2643383279 5028841971 6939937510
5820974944 5923078164 0628620899 8628034825 3421170679
8214808651 3282306647 0938446095 5058223172 5359408128
4811174502 8410270193 8521105559 6446229489 5493038196
4428810975 6659334461 2847564823 3786783165 2712019091
4564856692 3460348610 4543266482 1339360726 0249141273
7245870066 0631558817 4881520920 9628292540 9171536436
7892590360 0113305305 4882046652 1384146951 9415116094
3305727036 5759591953 0921861173 8193261179 3105118548
0744623799 6274956735 1885752724 8912279381 8301194912
9833673362 4406566430 8602139494 6395224737 1907021798
6094370277 0539217176 2931767523 8467481846 7669405132
0005681271 4526356082 7785771342 7577896091 7363717872
1468440901 2249534301 4654958537 1050792279 6892589235
4201995611 2129021960 8640344181 5981362977 4771309960
5187072113 4999999837 2978049951 0597317328 1609631859
5024459455 3469083026 4252230825 3344685035 2619311881
7101000313 7838752886 5875332083 8142061717 7669147303
5982534904 2875546873 1159562863 8823537875 9375195778
1857780532 1712268066 1300192787 6611195909 2164201989

3809525720 1065485863 2788659361 5338182796 8230301952
0353018529 6899577362 2599413891 2497217752 8347913151
5574857242 4541506959 5082953311 6861727855 8890750983
8175463746 4939319255 0604009277 0167113900 9848824012
8583616035 6370766010 4710181942 9555961989 4676783744
9448255379 7747268471 0404753464 6208046684 2590694912
9331367702 8989152104 7521620569 6602405803 8150193511
2533824300 3558764024 7496473263 9141992726 0426992279
6782354781 6360093417 2164121992 4586315030 2861829745
5570674983 8505494588 5869269956 9092721079 7509302955
3211653449 8720275596 0236480665 4991198818 3479775356
6369807426 5425278625 5181841757 4672890977 7727938000
8164706001 6145249192 1732172147 7235014144 1973568548
1613611573 5255213347 5741849468 4385233239 0739414333
4547762416 8625189835 6948556209 9219222184 2725502542
5688767179 0494601653 4668049886 2723279178 6085784383
8279679766 8145410095 3883786360 9506800642 2512520511
7392984896 0841284886 2694560424 1965285022 2106611863
0674427862 2039194945 0471237137 8696095636 4371917287
4677646575 7396241389 0865832645 9958133904 7802759009
------------------------------------------------------

PIfast的文件内容,前39行
--------------------------------------------------------
Program : PiFast version 4.3 (fix 1), by Xavier Gourdon
Computation of 1000000 digits of Pi
Method used : Chudnovsky
Size of FFT : 128 K
Physical memory used : ~ 7769 K
Disk memory used : ~ 0.00 Meg
------------------------------------------------------------
Computation run information :

Start : Wed May 09 14:26:38 2012
End : Wed May 09 14:26:39 2012
Duration : 1.30 seconds
============================================================
Total computation time : 1.30 seconds (~ 0.00 hours)
============================================================
Pi with 1000000 digits :
Pi = 3.
1415926535 8979323846 2643383279 5028841971 6939937510 : 50
5820974944 5923078164 0628620899 8628034825 3421170679 : 100
8214808651 3282306647 0938446095 5058223172 5359408128 : 150
4811174502 8410270193 8521105559 6446229489 5493038196 : 200
4428810975 6659334461 2847564823 3786783165 2712019091 : 250
4564856692 3460348610 4543266482 1339360726 0249141273 : 300
7245870066 0631558817 4881520920 9628292540 9171536436 : 350
7892590360 0113305305 4882046652 1384146951 9415116094 : 400
3305727036 5759591953 0921861173 8193261179 3105118548 : 450
0744623799 6274956735 1885752724 8912279381 8301194912 : 500

9833673362 4406566430 8602139494 6395224737 1907021798 : 550
6094370277 0539217176 2931767523 8467481846 7669405132 : 600
0005681271 4526356082 7785771342 7577896091 7363717872 : 650
1468440901 2249534301 4654958537 1050792279 6892589235 : 700
4201995611 2129021960 8640344181 5981362977 4771309960 : 750
5187072113 4999999837 2978049951 0597317328 1609631859 : 800
5024459455 3469083026 4252230825 3344685035 2619311881 : 850
7101000313 7838752886 5875332083 8142061717 7669147303 : 900
5982534904 2875546873 1159562863 8823537875 9375195778 : 950
1857780532 1712268066 1300192787 6611195909 2164201989 : 1000

----------------------------------------------------------------
下面我们重点分析第一个文件。
在这个文件中,总共有17个不同的字符。各个字符出现的次数和huffman编码如下,可以看到,数字‘0’到‘9’出现次数最多,其次是空格,再次是回车和换行符,其他字符则仅仅出现一次。
文件中包括1176511个字符,编码后共有4351792个比特,编码后,每个字符的平均码长为 5371792/1176511=3.6989。

我们香农公式(如下),以字符为单位,计算这个文件的信息熵,结果显示,平均每个字符只需要3.5953比特来表示。可以看到。我的这个程序的编码已经非常接近其信熵极限了。
sum(-p[i] * log2(p[i]),

注: x[i]表示字符i出现的概率。




字符 次数 huffman_code
[0a] 22022 100011
[0d] 22022 10000
[' '] 83886 1001
['.'] 1 10001000
['0'] 104789 1101
['1'] 104588 1011
['2'] 104939 1111
['3'] 105121 010
['4'] 104953 000
['5'] 105273 011
['6'] 104352 1010
['7'] 104691 1100
['8'] 104884 1110
['9'] 104987 001
['='] 1 10001001
['I'] 1 10001010
['P'] 1 10001011
回复
W170532934 2012-05-09
参考7zip,哈哈,希望楼主写出优秀的压缩软件出来
回复
Vanepin 2012-05-09
速度和性能得双重考虑吧。
回复
CandPointer 2012-05-09


http://www.maximumcompression.com/index.html


著名的,各种压缩对比的。


uharc 比较经典。2000年左右。盗版CD 光盘流行,那些盗版游戏啊,很多安装过程,会有 黑的cmd窗口弹出,一堆 uharc 的解压。。。

盗版商们选择 uharc, 说明它久经考验,压缩率高。
回复
jackyjkchen 2012-05-09
理论上最好的压缩算法是算术压缩算法,但是速度奇慢无比
回复
jackyjkchen 2012-05-09
[Quote=引用 4 楼 的回复:]
百度贴吧中看到《牛羚的压缩算法tutorial》比较不错,mark。
试了一下,7zip多我的这个文件压缩比低于我的程序。现在压缩率最强的PAQ算法,不过速度也出奇的慢。
[/Quote]

lzma/lzma2压缩比比rar高不少的,在实用字典压缩算法里,lzma/lzma2算是压缩比最高的了,接近信息熵了

楼主自其实可以试一下,你用自己的算法压缩一个比较大的文件(压缩后仍超过100MB),然后用winhex之类的文件分析功能算一下每个byte字符的出现率,如果能像7zip那样0x00-0xFF每个出现几乎都一样多,那就是比较牛了

Winrar特别是zip,都还能看到明显的波动
回复
liangbch 2012-05-09
百度贴吧中看到《牛羚的压缩算法tutorial》比较不错,mark。
试了一下,7zip多我的这个文件压缩比低于我的程序。现在压缩率最强的PAQ算法,不过速度也出奇的慢。
回复
赵4老师 2012-05-09
楼主可以参考7zip源代码。
回复
flyrack 2012-05-09
据说7zip的压缩比比rar高 不过也没流行起来额
回复
liangbch 2012-05-09
这里给出资源 http://download.csdn.net/detail/liangbch/4288748,不需要积分,随便下载。
回复
发动态
发帖子
C语言
创建于2007-09-28

6.2w+

社区成员

C语言相关问题讨论
申请成为版主
社区公告
暂无公告