基于哈夫曼树的文件压缩解压程序.doc
《基于哈夫曼树的文件压缩解压程序.doc》由会员分享,可在线阅读,更多相关《基于哈夫曼树的文件压缩解压程序.doc(26页珍藏版)》请在沃文网上搜索。
1、软件课程设计报告基于哈夫曼树的文件压缩/解压程序 计算机科学学院专业班号 2009-10-20一 需求分析1.课题要求(实现文件的压缩与解压并计算压缩率)A.描述压缩基本符号的选择方法B.运行时压缩原文件的规模应不小于5KC.提供恢复文件与原文件相同性对比功能2.设计目标A软件名称:基于哈夫曼编码的文件压缩实用程序系统B软件组成:Winhfm.exe dosHfm.exe C制作平台及相关调试工具: Windows2000sp4 Borland C+ Builder 6Dev-C+ 4.9.6.0 UltraEdit-32D运行环境:dos/win98/winMe/win2K/winxpE性能
2、特点:1. 软件由两个可执行文件组成,各具特点DosHfm.exe为dos系统应用程序,体积小,高效快捷,适用范围广。WinHfm.exe 为windows应用程序,界面友好,使用方便。2. 对单字节(256叶子)进行哈夫曼编码,压缩率良好2. 使用二级缓冲压缩/解压技术,速度比一般算法高75%以上3可压缩最大体积为4G的文件,达到Fat32文件系统极限4. 文件索引体积比常规算法小50%5压缩过程中显示压缩进度并有相关信息提示6WinHfm.exe可图形显示源文件的哈夫曼编码构造树二概要设计1.相关函数介绍 dos版本程序下的有关函数.void Haffman(int nodeCode,in
3、t length,int sum,vector pair &hfmCode,vector &lchild,vector &rchild)哈夫曼树编码递归函数void indexSearch(int nodeCode,vector &lchild,vector &rchild,vector &index,vector&code)索引编码递归函数void makeIndex(int nodeCode,int &tt,vector &index,int &indexNum,list &code,vector &lchild,vector &rchild)索引解码递归函数void Compress()
4、压缩函数void UnCompress()解压缩函数windows版本程序下的新增函数 AnsiString ShowNowTime()计算当前时间(精确到毫秒,以字符串返回)。.void search(int nodeCode,int &i,vector &lchild,vector &rchild)递归计算每个节点的X坐标. void searchdraw(int nodeCode,int height,vector &lchild,vector &rchild)递归做图函数.void _fastcall TForm1:Paga1OnDrawTab(TCustomTabControl *C
5、ontrol,int TabIndex, const TRect &Rect, bool Active)PageControl的标签页的色彩描绘void _fastcall TForm1:CompareFiles(TObject *Sender)文件比对函数 当然还有一些相关按钮的响应函数,在此不在赘述,详见程序清单部分 函数调用示意图三 详细设计1. 压缩算法部分A核心算法:文件由若干个字节组成,一个字节有8bits,故有28=256种字节构成形式,对应字节码为0-255。按此256种字节出现频率可构造haffman树进行重新编码,编码后每字节的新编码平均长度将8,输出前8位(用&操作可屏蔽
6、掉后24位),此时缓冲器清除前8位(a=a8),然后一次性读入B的代码置入a中,此时a容量为3+15=18位,此时输出前8位,现在 a=10位仍然大于8,在输出8位,剩余2位与下面的编码继续组合输出。显然,无论在运算时间上和存储空间上,后者均占明显优势。当然后者出现了&与运算 王浩面向对象程序设计35-36页2 沈美明 温冬蝉ibm-pc汇编语言程度设计61-62页,而这样的运算相当于汇编语言的AND与SAL2指令,是非常高效迅速的,比从内存读编码快捷,且操作次数也少的多。F写入算法另一角度的优化使用二级1024K缓冲器在写入编码的过程中,宏观的过程是:以字节形式读取原文件,然后找到该字节的编
7、码,进而以字节形式写到压缩文件中去。不停的字节读写会给cpu带来频繁的中断并且硬盘的磁头来回在磁道扇区中移动,减慢了速度。而如果以数据块的形式读写则可以有效地利用到DMA通讯 戴梅萼 史嘉权微型计算机技术及应用第二版199-201页,减少了cpu中断,并使硬盘磁头连续移动,显著加快了速度。而c+语言的iofstream类的read()与write()成员函数为此思想的实现提供了可能。 所以,可以开辟一个1024K的缓冲区,先将文件前1024K的字节读入内存缓冲区中(在本设计中,这称为二级缓冲器),编码后的字节也不急于写入文件中,而是先写到另一个二级缓冲区中,当其足够1024K时再以数据块的形式
8、写到压缩文件中。然后清空缓冲区,继续做下一个1024K的编码写入。而缓冲区本身,其实就是二个整形数组,read_buffer1048576和write_buffer1048576。不过这样的数组声明已经太大了,可以用STL的vector向量类来代替这个数组(vector结构实际也就是一个封装了的加强型数组)。一级缓冲器在微观上解决了写编码速度的问题,而二级缓冲器则在宏观上改善了写编码的问题,两者关系的嵌套的关系,实际的程序主结构也就是一个二重循环,外层控制二级缓冲器,内层控制一级缓冲器。G一些细节问题采用以单字节为单位构造哈夫曼树,这样数的规模不会太过庞大,构造的时间比较快,并且有比较良好的压
9、缩率(详细的压缩报告见附录二)。如果以双字节构造,则可能出现叶子数为65536的哈夫曼树,虽然压缩率可以得到相对提高,但已经提升不明显,而整个的压缩过程将变的缓慢。如果进行智能识别频率采样,一方面采样过程又要花费一定的时间,另一方面,哈夫曼树本身的结构也决定了这样做并不划算,也给解压缩和写入索引带来了许多麻烦。用left和right数组来描述一颗二叉树而没有用指针,是为了避免指针可能带来的由叶子到根的反向建树的麻烦;另一方面,树的节点和叶子数目基本确定,没太多必要使用灵活的指针和相关的内存分配语句。编码写出后,内层缓冲器可能剩几个bit,没有达到8bit,此时应补0或1以凑齐8位再写出。文件的
10、大小也不大可能正好被1024K整除,要考虑到最后的剩余部分字节的编码,即要计算好最后的实际字节读取个数,fstream的成员函数gcount() 王浩 面向对象程序设计 第245页能解决这个问题。如果把整个压缩过程作为一个函数的话,二级缓冲区的定义最好在函数外作为全局量定义,否则可能栈溢出。2解压缩算法部分A.基于字符匹配的解压算法现在从已压缩过的文件中读取一段代码,如”1001011101”,哈夫曼树结构入图,先读如第一个字节”10010111”,先取出“1”,在ABCD中均无这个编码;于是再取出“0”和刚才的“1”组成“01”,仍无此编码;再取出“0”,组成“010”,发现B叶子的编码与之
11、相等,此时解码得B,输出到解码文件中,以此类推。这是最容易想到的方法,但效率很低。首先,取出一个编码后要和所有叶子的编码比对;其次,编码比对是基于字符串的比对,比较慢。对于前者的改进可以通过:1.一旦比对成功就不再和剩下的比对2.按照编码的长度之后长度相同的编码比对等等。而后者的改进则出现了B算法。B.基于数值匹配的算法既然字符比对比较慢,我们可以把编码用2个整数表示,前者是编码的十进制值,后者是编码长度。这样只和编码长度相等的十进制相比就可以了。这样把字符串的比较优化为数字比较,据测算,速度提高了约10倍。但是这两种算法都基于与叶子编码比较,相当于不断的尝试,解压速度很难突破100KB/s。
12、C.基于循径法既然所有的编码都隐藏在一个树中,那么根据遇0向左走遇1向右走的方法自然就能很容易地找到叶子,从而得到解码。仍如前例,读入”1”向右走,发现不是叶子,继续;读入”0”向左走,发现不是叶子,继续;读入”0”向左走,发现是叶子B,即可解码为B写入解码文件。也就是说,循径法充分地利用了每次读进来的编码,能够以确定的路径找到叶子而不需要尝试。不过要使用这种方法,就必须想建立一个原文件的哈夫曼树,详见索引算法部分。使用此方法后速度有极大飞跃。D.缓冲输入输出和压缩时采用1M二级缓冲相同,如果的压缩时也采用此技术,也会有很大的速度优化,当然程序也变得更加复杂。E.细节问题读入和写出仍然基于字节
13、单位,基于位的操作同样要在程序内部通过位运算完成。最后一个字节在解码时必须注意到压缩时最后一个字节是用”0”或”1”填充的,要避免多余解码,可以在索引中写入文件大小,详见下节。3文件索引算法A. 简介由解压缩的算法可知,一个压缩了的文件解压缩的前提是知道原文件的具体每个字节的编码。这些信息称为索引。上页的细节问题中提到最好在压缩后的文件中标出原文件的长度,这也是索引信息。一般索引信息放在文件的最前部分。B. 写入编码法最直接的方法就是,把每个字节的编码写到压缩后的文件中去。入图,先写入A及其编码0,接着是B及编码100,C101,D11。即:01000001 0 01000010 100 01
14、000011 101 01000100 11A B C D当然直接这样写会给阅读信息造成困难,因为计算机不知道A的编码是几位,它读入0后就不知道是否下一位究竟是A的编码还是B的ASCII的开始。所以我们还得标识出编码的长度A1 B3 C3 D2,即01000001 00000000 0 01000010 00000011 100 01000011 00000011 101 01000100 00000010 11A 长度0 B 长度3 C 长度3 D 长度2这样的确是区别开了,没有歧义,可是编码变长了,我们可以规定叶子是按顺序排列的,此时编码就变短了,即:00000000 0 00000011
15、 100 00000011 101 00000010 11A的长度 B的长度 C的长度 D的长度事实上最大一共有256个叶子,计算机依次读256次就可以了。这样索引占用的长度一般是512K左右。不过一旦一个文件只有5片叶子,也得有256个字节来标识编码长度,这在压缩只有几个字节的小文件时,反而文件会扩大几十倍。C. 写入树结构法如果我们知道原文件的哈夫曼树的结构,也自然就可获知每个叶子的编码,那么,把这棵树的结构作为索引存入文件也可以,如果树可大可小,索引的长度也会可大可小,解决了B方法索引长度始终大于256字节的问题。如上图,如果非叶子节点为#,这个树的结构编码 胡学钢 王浩 软件系列课程实
16、践教程第49页“扩展二叉树”就是”#A.#B.C.D.”而且哈夫曼树有一个性质,如果一个节点有左孩子,就一定有右孩子,如果没有左孩子,也必然没有右孩子。由此没有必要再用点号来表示叶子了,因而树结构简化成”#A#B#CD”,再用1表示叶子,0表示非叶子,则为”01001011”,这就是它的存储实现。如下:00000100 01000001 01000010 01000011 01000100 01001011有4个节点 A B C D 树结构对于一棵完整的有256个叶子的haffman树,大约需要320字节就可以存储它了。比前面的方法节省了38%。当然,要使用这种方法,就必须在压缩时用一个递归函
17、数来遍历这棵树以获得树结构编码。D.关于索引的解码AB两种建索引的方法都很方便于索引的解码,但空间占用大后者灵活性差,而若使用C方法,则索引的解码也成了问题。换句话说,我们得由“01001011”来还原出一棵haffman树。本系统是这样做的,首先得把树结构编码从文件中读到一个数组中,把叶子编号读到另一个数组中,然后由这两个数组用递归的方法造出树。然后由这棵树再求出每个叶子的编码。当然这一步可以略去了,因为解压缩采用寻径法,不需要再求每个叶子的具体编码了。E.相关细节问题为了给正文部分解码代码方便并显示解码进度,本系统在压缩的文件开头的四个字节记录了原文件的长度。索引中,节点的顺序和结构树的顺
- 1.请仔细阅读文档,确保文档完整性,对于不预览、不比对内容而直接下载带来的问题本站不予受理。
- 2.下载的文档,不会出现我们的网址水印。
- 3、该文档所得收入(下载+内容+预览)归上传者、原创作者;如果您是本文档原作者,请点此认领!既往收益都归您。
下载文档到电脑,查找使用更方便
20 积分
下载 | 加入VIP,下载更划算! |
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 哈夫曼树 文件 压缩 解压 程序