文件压缩

文件压缩


开发平台Visual Studio 2015


开发技术:堆排序,哈夫曼树


项目描述


1.统计文件中字符出现的次数,利用数据结构中的堆建造Huffman树,字符出现次数多的编码短,出现次数少的编码长;
2.根据建造好的Huffman树形成编码,以对文件进行压缩;
3.将文件中出现的字符以及他们出现的次数写入配置文件,以便后续的解压缩;
4.根据配置文件读取相关信息,重建Huffman树,对压缩后的文件进行译码。


先看如下两张图,了解一点背景知识:

图一


图二


哈弗曼树的原理:


如果有一些结点的权值分别是1,2,3,4,5,6 ,构建出来的哈弗曼树如下图:



思想每次从数组中取两个当前权值最小的数去创建结点,并作为叶子结点,它们的根节点的权值是两者之和,把它再放回数组,第一次选择1,2;第二次选择3,3;第三次选择4,5;~~~~ ~~


文件压缩的原理:


文件压缩真正要用到的是哈夫曼编码,对于上面那棵树,它的哈弗曼编码怎么来的呢?根据上图,同理可得:


权值为1的结点的哈夫曼编码就是1000
权值为2的结点的哈夫曼编码就是1001
权值为3的结点的哈夫曼编码就是101
权值为4的结点的哈夫曼编码就是00
权值为5的结点的哈夫曼编码就是01
权值为6的结点的哈夫曼编码就是11
注意:哈夫曼编码只是对叶子节点编码

可以发现一个规律权值越小的,它的哈夫曼编码越长,权值越大的,哈夫曼编码越短。
那么如何运用到文件压缩中呢?


假设有一个文件的内容是“abbcccdddd“,‘a’出现的次数是1,‘b’出现的次数是2,‘c’出现的次数是3,‘d’出现的次数是4,以各字符出现的次数构建一个哈夫曼树,并为各字符编码,结果是:
‘a’:100; ‘b’:101; ‘c’:11; ‘d’:0;

如下图:


以编码的形式按照原字符的顺序写入压缩文件,如下
1001011011111110000


这里0或1代表一个二进制位,那压缩文件是多大呢?压缩文件一共19个bit位,不够的位补齐,只占3个字节
原文件是多大呢?原字符串“abbcccdddd“10个字符,占10个字节;这就是文件压缩的原理。


项目主要思路


1.统计:首先读取一个文件,统计出256个字符中各个字符出现的次数以及字符出现的总数;

2.建树:按照字符出现的次数,并以次数作为权值建立哈夫曼编码树建好树后找出各个字符的编码;

3.压缩:再次读取文件,按照该字符对应的编码压缩文件;

4.加工:将文件的长度,文件中各个字符以及它们出现的次数写进配置文件中;

5.解压:利用压缩文件和配置文件恢复出原文件;

6.测试:首先观察解压的文件和原文件是否相同,再通过Beyond Compare 4软件进行对比,验证程序的正确性。


文件压缩的过程:


首先要统计待压缩文件中各字符出现的次数,然后构造哈弗曼编码,把编码写入压缩文件,不够一个字节的就在后面补零,因为要解压缩,所以还得写一个配置文件,配置文件里面写每个字符出现的次数。


具体过程:

a.读取文件,将每个字符,该字符出现的次数和权值构成哈夫曼树;

b.哈夫曼树是利用小堆构成,字符出现次数少的节点指针存在堆顶,出现次数多的在堆底;

c.每次取堆顶的两个数,再将两个数相加进堆,直到堆被取完,这时哈夫曼树也建成;

d.从哈夫曼树中获取哈夫曼编码,然后再根据整个字符数组来获取出现了字符的编码;

e.获取编码后每次凑满8位就将编码串写入到压缩文件;

f.写好配置文件,统计每个字符及其出现次数,保存到配置文件中。


文件解压的过程:


先去读配置文件,构建哈弗曼树和哈弗曼编码,用压缩文件里的编码去哈弗曼树里面找,找到叶子结点,就把叶子节点的字符写入解压缩文件中。


具体过程:

a.读取配置文件,统计所有字符的个数;

b.构建哈夫曼树,读解压缩文件,将所读到的编码字符的这个节点所含的字符写入到解压缩文件中,直到将压缩文件读完;

c.解压缩完成之后利用Beyond Compare 4软件,进行文件的测试。



项目测试:


通过Beyond Compare 4软件,对文件压缩前和压缩后的内容进行对比,验证程序的正确性。性能测试,在release版本下会更高效一些,时间会缩短很多因为 Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序,也就是博主编程用的版本;Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。


完整项目代码链接https://github.com/leiyufei/wenjianyasuo1


运行结果:


测试用例


原文件为“穿过落雁修竹,看过月升日落你说总有一日会名扬天下实现你抱负”(名为1.txt),经过压缩(1.huff),解压后(1_Com.txt)的文件的内容如下:

图三

图四

项目中出现的问题


1测试的时候发现如果待压缩文件中出现中文,程序就会崩溃,将错误定位到构造哈弗曼编码的函数处,通过单步调试发现是数组越界所致,因为如果只是字符,它的范围是-128~127,程序中通过一个char类型的变量作为数组的下标(0~127),是没有问题的,但汉字的编码是两个字节(只能收录2万多的汉字,但在本项目中已经够用了,这里不进行深究),所以会发生数组越界,解决方法是将char强转为unsigned char可表示范围为0~255


2为什么要使用配置文件?

在项目中,将字符对应的编码转化为位,在unsigned char中填充位,填满后就写入到压缩文件中。

问题1:最后一个字节是不是很有可能没有填满,该如何判断他是否填满以及填了几个字符的编码?

问题2:若依次压缩一些文件,压缩完后再去解压,那么编码此时已经没有了,该如何解压?

上面的两个问题通过配置文件解决,假如要压缩的文件叫xxx,那么可生成一个xxx.config的配置文件,在该配置文件中写入<文件的总长度>(恢复时知道应该恢复多少个字符),<char-times>(字符以及出现的次数,用于解压时重建哈夫曼树),利用配置文件即可解决这两个问题


3在文件恢复的时候需要注意哪些问题?

有些特殊字符的处理需要注意一下,比如 '\n',我的程序中有一个函数就是读取一行字符,但是若是该字符本身就是'\n'呢,我们写入的<char-times>的char就是\n呢?那么就比较棘手了,对于这个问题,千万不能漏掉,否则就不能恢复出原来的文件。读取配置文件的时候若读到’\n‘,则说明该字符就是'\n',应该继续读取它的次数。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
综合实验: 1. 问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站编写一个哈夫曼码的编/译码系统。 2. 基本要求 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree。 (2) E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree读入),对文件ToBeTran的正文进行编码,然后将结果存入文件CodeFile。 (3) D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile代码进行译码,结果存入文件Textfile。 (4) P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin。 (5) T:印哈夫曼树(Tree printing)。将已在内存的哈夫曼树以直观的方式(比如树)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint 。 3. 测试数据 用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAME IS MY FAVORITE”。 字符 A B C D E F G H I J K L M 频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 57 63 15 1 48 51 80 23 8 18 1 16 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值