项目(1)——文件压缩

项目——文件压缩

一、简历上写的

 

测试用例:77M大小的一个文本文件

压缩:  33秒

解压缩: 6秒

 

文件压缩

项目简介: 对文件进行无损压缩,节省空间,用在网络传输中,节省流量传输宽带

开发环境: Linux/Windows,C++,VS2013,vim,gdb,g++,make

主要技术: HuffmanTree,Heap,字符串操作,位操作

项目描述: 压缩,解压缩过程及原理

压缩:读取待压缩文件中字符出现次数,使用贪心算法建立HuffmanTree

每个字符都有一串对应Huffman编码,出现频率高的字符编码短,反之则长,达到文件压缩目的

将每个字符对应的编码转换成对应的二进制位表示并写入压缩文件

将对应字符及对应字符出现次数写入配置文件,方便解压缩时重建HuffmanTree

解压缩:根据配置文件重建HuffmanTree

读取压缩文件,解析二进制位,对应在HuffmanTree中解析出对应的字符,还原文件内容

代码托管: https://github.com/18392517817/_DataStruct_/tree/master/FileCompress

 

二、你这个文件压缩工具是怎么实现的?为什么要实现这个工具?

 

(1)、为什么要实现这个文件压缩?

压缩软件是为了解决早期计算机储存空间不足而设计的。文件被压缩后使用的储存空间会较少。同时压缩后文件编码被改变,就不能对原文件进行编辑,更改,所以压缩软件都有解压缩功能,即将文件恢复成原来的样子。

但它还有两项用途:

一,对于网管:减少网站使用的储存空间,服务器的空间是付费的。

二,对于个人:简单的文件加密。

目前互联网络上大家常用的FTP文件服务器上的文件大多属于压缩文件,文件下载后必须先解压缩才能够使用;另外在使用电子邮件附加文件功能的时候,最好也能事先对附加文件进行压缩处理。这样做的结果,除了减轻网络的负荷,更能省时省钱。

目前网络上有两种常见的压缩格式:一种是Zip,另一种是EXE。其中Zip的压缩文件可以通过WinZip这套解压缩工具进行解压缩,而EXE则是属于自解压文件,只要用鼠标双击这类下载后的文件图标,便可以自动解压缩。因为EXE文件内含解压缩程序,因此会比Zip略大一些。若想充分考虑到文件容量的大小,其实Zip是一个较佳的选择

 

(2)、文件压缩实现的思路:

我们把文件中一定位长的值看作是符号,比如把8位长的256种值,也就是字节的256种值看作是符号。我们根据这些符号在文件中出现的频率,对这些符号重新编码。对于出现次数非常多的,我们用较少的位来表示,对于出现次数非常少的,我们用较多的位来表示。这样一来,文件的一些部分位数变少了,一些部分位数变多了,由于变小的部分比变大的部分多,所以整个文件的大小还是会减小,所以文件得到了压缩。

具体步骤:

第一步:

要进行Huffman编码,首先要把整个文件读一遍,在读的过程中,统计每个符号的出现次数(我们把字节的256种值看作是256种符号)。然后根据符号的出现次数,建立Huffman树,通过Huffman树得到每个符号的新的编码。对于文件中出现次数较多的符号,它的Huffman编码的位数比较少。对于文件中出现次数较少的符号,它的Huffman编码的位数比较多。然后把文件中的每个字节替换成他们新的编码。

 

第二步:建立Huffman树

1、把所有符号看成是一个结点,并且该结点的值为它的出现次数。进一步把这些结点看成是只有一个结点的树。

2、每次从所有树中找出值最小的两个树,为这两个树建立一个父结点,然后这两个树和它们的父结点组成一个新的树,这个新的树的值为它的两个子树的值的和。如此往复,直到最后所有的树变成了一棵树。我们就得到了一棵Huffman树。

3、通过Huffman树得到Huffman编码:

这棵Huffman树,是一棵二叉树,它的所有叶子结点就是所有的符号,它的中间结点是在产生Huffman树的过程中不断建立的。我们在Huffman树的所有父结点到它的左子结点的路径上标上0,右子结点的路径上标上1。

4、现在我们从根节点开始,到所有叶子结点的路径,就是一个0和1的序列。我们用根结点到一个叶子结点路径上的0和1的序列,作为这个叶子结点的Huffman编码。

第三步:保存配置文件

为了在解压缩的时候,得到压缩时所使用的Huffman树,我们需要在压缩文件中,保存树的信息,也就是保存每个符号的出现次数的信息。

 

总结:

1、我们可以看到,Huffman树的建立方法就保证了,出现次数多的符号,得到的Huffman编码位数少,出现次数少的符号,得到的Huffman编码位数多。

各个符号的Huffman编码的长度不一,也就是变长编码。对于变长编码,可能会遇到一个问题,就是重新编码的文件中可能会无法如区分这些编码。

比如,a的编码为000,b的编码为0001,c的编码为1,那么当遇到0001时,就不知道0001代表ac,还是代表b。出现这种问题的原因是a的编码是b的编码的前缀。

由于Huffman编码为根结点到叶子结点路径上的0和1的序列,而一个叶子结点的路径不可能是另一个叶子结点路径的前缀,所以一个Huffman编码不可能为另一个Huffman编码的前缀,这就保证了Huffman编码是可以区分的。

2、压缩:

读文件,统计每个符号的出现次数。根据每个符号的出现次数,建立Huffman树,得到每个符号的Huffman编码。将每个符号的出现次数的信息保存在压缩文件中,将文件中的每个符号替换成它的Huffman编码,并输出。

3、解压缩:

得到保存在压缩文件中的,每个符号的出现次数的信息。根据每个符号的出现次数,建立Huffman树,得到每个符号的Huffman编码。将压缩文件中的每个Huffman编码替换成它对应的符号,并输出。

 

三、分析一下你的Huffman树的压缩算法的适用范围?以及你的huffman压缩工具的优缺点?

在压缩文件的时候,人们不禁会产生一些新想法或者遇到一些疑问:

 

1、是否可以对压缩后的数据再次压缩?

答:就操作上来说,当然能反复编码,但通过对本文例子中得到的新编码再次操作后会发现,结果是不会有任何变化的。压缩的实质,在于消除特定字符分布上的不均衡,通过将短码分配给高频字符,而长码对应低频字符实现长度上的优化。而数据经过一次压缩后,字符的分布已经几乎平均化了,很难更进一步的压缩了。

 

2、当2 n 的n变大后,遇到A:1010,B:10这样的情况,如何解读10101010?

答:而第二个问题描述的情况是不会出现的的。从构造霍夫曼树操作上可以看到,一个字符无法在另一个字符的上层(每个字符的huffman编码都是从根节点到这个字符所对应的节点的路径,而每个字符都在叶子节点上,所以一个字符对应的huffman编码不可能是另一个字符对应的huffman编码的前缀)。只要操作正确,就一定可以构造出唯一的代码表,不存在歧义。

3、还有一个有趣的问题是:虽然把40字节的内容压缩到了34字节,但需要将相应的码表一并发送给接收方(没有对应码表,无法解压)。这不反而使得压缩后的数据比压缩前的还要长?

答:事实也确实如此。本文例子中,真正的最终结果体积是大于原文的。但这不意味了算法错误。这是因为“n”过小。总长度的不够使得节省出来的那部分容量还不足以弥补码表本身的储存空间。实际应用中,如果你非要去压缩一个只有几个字节的文件,得到的压缩包也经常会大于文件本身。

通常,压缩软件会在每压缩4kb到32kb数据后,重新生成并保存一个霍夫曼树。

 

4、当分块过大时,统计上的整体平均,会掩盖小区域内的极度不平均,损失了压缩的空间。比如存在一个这样的文件:AAAAA……AAAAA(一万个)BBBBB……BBBBB(一万个)……ZZZZ(一万个)。

答:如果从整体上进行霍夫曼树操作,将不会产生任何压缩,但是这时候我们把它分成26块,压缩并各自保存相应的重新编码的霍夫曼树,压缩率将非常惊人,约等于12.5%。在现实的文本中,英语字母使用频率各不相同,而且差别很大。有着很高的不平均度。所以大部分压缩软件对文本文件依然有着很高的压缩率(不平均度越是高,压缩率越大)。

5、你的huffman压缩工具的优缺点?

优点:对数据不平均度高的文件,压缩效率高。对文本文件的压缩率高。只要操作正确,都能保证构成唯一的编码表。

缺点:对图片像素帧点分布平均集中的文件没有很好地压缩效果,对MP3音频文件、视频文件及文件夹不能达到压缩效果。

四、你是做测试的,那你对你这个项目都用了哪些测试用例?

1、对不同文件大小进行测试:对10kb之内的小文件,利用Beyound Compare进行对比,结果正确。对8M的文件,结果正确,但压缩时间明显加长,说明算法有待改进。

2、对不同文件格式和不同文件类型进行测试:

(1)、不同文件格式:

obj:二进制文件。

TXT:文本文件。

exe:可执行文件,应用程序。

doc word: wps文件。

xls execl:表格文件。

MP3:音乐文件。

PPT:幻灯片文件。

html:网页文件。

rar/zip:压缩文件。

git:动画文件。

com:系统可执行文件。

dll:动态链接文件。

dot:模板文件。

 

(2)、不同文件类型:

压缩图片:JPG的压缩率几乎是0,PNG和pdf虽然能压缩,但是压缩率非常低,说明这种算法并不适合压缩图片。(图片的像素帧点分布非常密集,这就导致数据分布平均度比较高,并不适合使用huffaman编码的方式进行压缩)。

压缩音乐:mp3格式的音乐和图片一样,用这种算法几乎没有压缩率。

压缩文件夹:程序挂掉,打开文件出错,说明这种压缩方式不能压缩文件夹。

 

3、总结:

文件类型:按内容来分主要有视频、音频、软件三大类。

文件格式(或文件类型):是指电脑为了存储信息而使用的对信息的特殊编码方式,是用于识别内部储存的资料。比如有的储存图片,有的储存程序,有的储存文字信息。每一类信息,都可以一种或多种文件格式保存在电脑存储中。每一种文件格式通常会有一种或多种扩展名可以用来识别,但也可能没有扩展名。扩展名可以帮助应用程序识别的文件格式。

五、你还知道那些文件压缩算法?

1、字典算法:

字典算法是最为简单的压缩算法之一。它是把文本中出现频率比较多的单词或词汇组合做成一个对应的字典列表,并用特殊代码来表示这个单词或词汇。例如:

有字典列表:

Chinese=00

People=01

China=02

源文本:I am a Chinese people,I am from China 压缩后的编码为:I am a 00 01,I am from 02。压缩编码后的长度显著缩小,这样的编码在游戏等专有名词比较多的程序中更容易出现。

 

 

2、LZ77算法

Lempel-Ziv压缩模式有许多不同的变量。基本压缩库有清晰的LZ77算法的实现(Lempel-Ziv,1977),执行的很好,源代码也非常容易理解。

LZ编码器能用来通用目标的压缩,特别对于文本文件执行的很好。

 

压缩:

从文件的开始到文件结束,一个字节一个字节的向后进行处理。用当前处理字节开始的串,和滑动窗口中的每个串进行匹配,寻找最长的匹配串。如果当前处理字节开始的串在窗口中有匹配串,就先输出一个标志位,表明下面是一个(之间的距离,匹配长度) 对,然后输出(之间的距离,匹配长度) 对,然后从刚才处理完的串之后的下一个字节,继续处理。如果当前处理字节开始的串在窗口中没有匹配串,就先输出一个标志位,表明下面是一个没有改动的字节,然后不做改动的输出当前处理字节,然后继续处理当前处理字节的下一个字节。

 

解压缩:

从文件开始到文件结束,每次先读一位标志位,通过这个标志位来判断下面是一个(之间的距离,匹配长度) 对,还是一个没有改动的字节。如果是一个(之间的距离,匹配长度)对,就读出固定位数的(之间的距离,匹配长度)对,然后根据对中的信息,将匹配串输出到当前位置。如果是一个没有改动的字节,就读出一个字节,然后输出这个字节。

 

我们可以看到,LZ77压缩时需要做大量的匹配工作,而解压缩时需要做的工作很少,也就是说解压缩相对于压缩将快的多。这对于需要进行一次压缩,多次解压缩的情况,是一个巨大的优点。

 

实际中,我们还将设定一个最小匹配长度,只有当两个串的匹配长度大于最小匹配长度时,我们才认为是一个匹配。我们举一个例子来说明这样做的原因。比如,“距离”使用15位,“长度”使用8位,那么“(之间的距离,匹配长度)对”将使用23位,也就是差1位3个字节。如果匹配长度小于3个字节的话,那么用“(之间的距离,匹配长度)对”进行替换的话,不但没有压缩,反而会增大,所以需要一个最小匹配长度。

 

 

3、固定位长算法

这种算法是把文本用需要的最少的位来进行压缩编码。

比 如八个十六进制数:1,2,3,4,5,6,7,8。转换为二进制为:00000001,00000010,00000011,00000100, 00000101,00000110,00000111,00001000。每个数只用到了低4位,而高4位没有用到(全为0),因此对低4位进行压缩编 码后得到:0001,0010,0011,0100,0101,0110,0111,1000。然后补充为字节得到:00010010, 00110100,01010110,01111000。所以原来的八个十六进制数缩短了一半,得到4个十六进制数:12,34,56,78。

这也是比较常见的压缩算法之一。

 

 

4、 RLE(Run Length Encoding)

RLE是一个针对无损压缩的非常简单的算法。它用重复字节和重复的次数来简单描述来代替重复的字节。尽管简单并且对于通常的压缩非常低效,但它有的时候却非常有用(例如,JPEG就使用它)。

 

举一个使用RLE算法来对一个数据流编码的例子,其中出现六次的符号‘93’已经用3个字节来代替:一个标记字节(‘0’在本例中)重复的次数(‘6’)和符号本身(‘93’)。RLE解码器遇到符号‘0’的时候,它表明后面的两个字节决定了需要输出哪个符号以及输出多少次。

这种压缩编码是一种变长的编码,RLE根据文本不同的具体情况会有不同的压缩编码变体与之相适应,以产生更大的压缩比率。

  变体1:重复次数+字符

文本字符串:A A A B B B C C C C D D D D,编码后得到:3 A 3 B 4 C 4 D。

  变体2:特殊字符+重复次数+字符

文本字符串:A A A A A B C C C C B C C C,编码后得到:B B 5 A B B 4 C B B 3 C。编码串的最开始说明特殊字符B,以后B后面跟着的数字就表示出重复的次数。

 

  变体3:把文本每个字节分组成块,每个字符最多重复 127 次。每个块以一个特殊字节开头。那个特殊字节的第 7 位如果被置位,那么剩下的7位数值就是后面的字符的重复次数。如果第 7 位没有被置位,那么剩下 7 位就是后面没有被压缩的字符的数量。例如:文本字符串:A A A A A B C D E F F F。编码后得到:85 A 4 B C D E 83 F(85H= 10000101B、4H= 00000100B、83H= 10000011B)。

实现RLE可以使用很多不同的方法。基本压缩库中详细实现的方式是非常有效的一个。一个特殊的标记字节用来指示重复节的开始,而不是对于重复非重复节都coding run。因此非重复节可以有任意长度而不被控制字节打断,除非指定的标记字节出现在非重复字节的稀有情况下。为了最优化效率,标记字节应该是输入流中最少出现的符号(或许就不存在)。

其他还有很多很多变体算法,这些算法在Winzip Winrar这些软件中也是经常用到的。

 

一个压缩文件是不是还可以用其他算法再继续压缩?

可以,但没要。压缩文件有极限值存在。高压一遍已经很接近这个值了,再压缩的话基本也就只有一丁点压缩率提升,甚至会增加体积。

 

六、你了解哪些常用的文件压缩工具?

haozip好压、kuaizip快压、winzip、winrar、7-zip

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值