基于哈夫曼树的文件压缩解压程序-示例文档.pdf
《基于哈夫曼树的文件压缩解压程序-示例文档.pdf》由会员分享,可在线阅读,更多相关《基于哈夫曼树的文件压缩解压程序-示例文档.pdf(26页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、软件课程设计报告基于哈夫曼树的文件压缩/解压程序计算机科学学院专业班号2009-10-20一需求分析1.课题要求(实现文件的压缩与解压并计算压缩率)A.描述压缩基本符号的选择方法B.运行时压缩原文件的规模应不小于 5KC.提供恢复文件与原文件相同性对比功能2.设计目标A 软件名称:基于哈夫曼编码的文件压缩实用程序系统B 软件组成:Winhfm.exe dosHfm.exeC 制作平台及相关调试工具:Windows2000sp4Borland C+Builder 6Dev-C+4.9.6.0UltraEdit-32D 运行环境:dos/win98/winMe/win2K/winxpE 性能特点:
2、1.软件由两个可执行文件组成,各具特点DosHfm.exe 为 dos 系统应用程序,体积小,高效快捷,适用范围广。WinHfm.exe 为 windows 应用程序,界面友好,使用方便。2.对单字节(256 叶子)进行哈夫曼编码,压缩率良好2.使用二级缓冲压缩/解压技术,速度比一般算法高 75%以上3可压缩最大体积为 4G 的文件,达到 Fat32 文件系统极限4.文件索引体积比常规算法小 50%5压缩过程中显示压缩进度并有相关信息提示26WinHfm.exe可图形显示源文件的哈夫曼编码构造树二概要设计1.相关函数介绍dos 版本程序下的有关函数.void Haffman(int nodeC
3、ode,int length,int sum,vector pair&hfmCode,vector&lchild,vector&rchild)哈夫曼树编码递归函数void indexSearch(int nodeCode,vector&lchild,vector&rchild,vector&index,vector&code)索引编码递归函数 voidmakeIndex(intnodeCode,int&tt,vector&index,int&indexNum,list&code,vector&lchild,vector&rchild)索引解码递归函数void Compress()压缩函数voi
4、d 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*Control,int T
5、abIndex,const TRect&Rect,bool Active)PageControl 的标签页的色彩描绘void _fastcall TForm1:CompareFiles(TObject*Sender)文件比对函数当然还有一些相关按钮的响应函数,在此不在赘述,详见程序清单部分3 函数调用示意图4三详细设计51.压缩算法部分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 位与下面的编码继续组合输出。显然,无论在运算时间上和存储空间上,后者均占明显优势。当然后者出现了&与运算1,而这样的运算相当于汇编语言的 AND 与 SAL2指令,是非常高效迅速的,比从内存读编码快捷,且操作次数也少的多。F 写入算法另一角度的优化使用二级 1024K 缓冲器在写入编码的过程中,宏观的过程是:以字节形式读取原文件,然后找到该字节的编码,进而以字节形式写到压缩文件中去。不停的字节读
7、写会给cpu 带来频繁的中断并且硬盘的磁头来回在磁道扇区中移动,减慢了速度。而如果以数据块的形式读写则可以有效地利用到 DMA 通讯2,减少了 cpu 中断,并使硬盘磁头连续移动,显著加快了速度。而 c+语言的 iofstream 类的read()与 write()成员函数为此思想的实现提供了可能。所以,可以开辟一个 1024K 的缓冲区,先将文件前 1024K 的字节读入内存缓冲区中(在本设计中,这称为二级缓冲器),编码后的字节也不急于写入1王浩面向对象程序设计35-36 页2沈美明 温冬蝉ibm-pc 汇编语言程度设计61-62 页2戴梅萼 史嘉权微型计算机技术及应用第二版 199-201
8、 页9文件中,而是先写到另一个二级缓冲区中,当其足够1024K 时再以数据块的形式写到压缩文件中。然后清空缓冲区,继续做下一个1024K 的编码写入。而 缓 冲 区 本 身,其 实 就 是 二 个 整 形 数 组,read_buffer1048576 和write_buffer1048576。不过这样的数组声明已经太大了,可以用STL 的vector 向量类来代替这个数组(vector 结构实际也就是一个封装了的加强型数组)。一级缓冲器在微观上解决了写编码速度的问题,而二级缓冲器则在宏观上改善了写编码的问题,两者关系的嵌套的关系,实际的程序主结构也就是一个二重循环,外层控制二级缓冲器,内层控制
9、一级缓冲器。G 一些细节问题采用以单字节为单位构造哈夫曼树,这样数的规模不会太过庞大,构造的时间比较快,并且有比较良好的压缩率(详细的压缩报告见附录二)。如果以双字节构造,则可能出现叶子数为 65536 的哈夫曼树,虽然压缩率可以得到相对提高,但已经提升不明显,而整个的压缩过程将变的缓慢。如果进行智能识别频率采样,一方面采样过程又要花费一定的时间,另一方面,哈夫曼树本身的结构也决定了这样做并不划算,也给解压缩和写入索引带来了许多麻烦。用 left和 right数组来描述一颗二叉树而没有用指针,是为了避免指针可能带来的由叶子到根的反向建树的麻烦;另一方面,树的节点和叶子数目基本确定,没太多必要使
10、用灵活的指针和相关的内存分配语句。编码写出后,内层缓冲器可能剩几个 bit,没有达到 8bit,此时应补010或1以凑齐 8 位再写出。文件的大小也不大可能正好被 1024K 整除,要考虑到最后的剩余部分字节的编码,即要计算好最后的实际字节读取个数,fstream 的成员函数gcount()1能解决这个问题。如果把整个压缩过程作为一个函数的话,二级缓冲区的定义最好在函数外作为全局量定义,否则可能栈溢出。2解压缩算法部分A.基于字符匹配的解压算法现在从已压缩过的文件中读取一段代码,如”1001011101”,哈夫曼树结构入图,先读如第一个字节”10010111”,先取出“1”,在 ABCD 中均
11、无这个编码;于是再取出“0”和刚才的“1”组成“01”,仍无此编码;再取出“0”,组成“010”,发现 B 叶子的编码与之相等,此时解码得 B,输出到解码文件中,以此类推。这是最容易想到的方法,但效率很低。首先,取出一个编码后要和所有叶子的编码比对;其次,编码比对是基于字符串的比对,比较慢。对于前者的改进可以通过:1.一旦比对成功就不再和剩下的比对 2.按照编码的长度之后长度相同的编码比对等等。而后者的改进则出现了 B 算法。B.基于数值匹配的算法1既然字符比对比较慢,我们可以把编码用 2 个整数表示,前者是编码的王浩 面向对象程序设计 第 245 页11十进制值,后者是编码长度。这样只和编码
12、长度相等的十进制相比就可以了。这样把字符串的比较优化为数字比较,据测算,速度提高了约 10 倍。但是这两种算法都基于与叶子编码比较,相当于不断的尝试,解压速度很难突破 100KB/s。C.基于循径法既然所有的编码都隐藏在一个树中,那么根据遇 0 向左走遇 1 向右走的方法自然就能很容易地找到叶子,从而得到解码。仍如前例,读入”1”向右走,发现不是叶子,继续;读入”0”向左走,发现不是叶子,继续;读入”0”向左走,发现是叶子 B,即可解码为 B 写入解码文件。也就是说,循径法充分地利用了每次读进来的编码,能够以确定的路径找到叶子而不需要尝试。不过要使用这种方法,就必须想建立一个原文件的哈夫曼树,
13、详见索引算法部分。使用此方法后速度有极大飞跃。D.缓冲输入输出和压缩时采用 1M 二级缓冲相同,如果的压缩时也采用此技术,也会有很大的速度优化,当然程序也变得更加复杂。E.细节问题读入和写出仍然基于字节单位,基于位的操作同样要在程序内部通过位运算完成。最后一个字节在解码时必须注意到压缩时最后一个字节是用”0”或”1”填充的,要避免多余解码,可以在索引中写入文件大小,详见下节。3文件索引算法A.简介12由解压缩的算法可知,一个压缩了的文件解压缩的前提是知道原文件的具体每个字节的编码。这些信息称为索引。上页的细节问题中提到最好在压缩后的文件中标出原文件的长度,这也是索引信息。一般索引信息放在文件的
14、最前部分。B.写入编码法最直接的方法就是,把每个字节的编码写到压缩后的文件中去。入图,先写入A及其编码 0,接着是B及编码100,C101,D11。即:01000001 0 01000010 100 01000011 101 01000100 11ABCD当然直接这样写会给阅读信息造成困难,因为计算机不知道A的编码是几位,它读入 0 后就不知道是否下一位究竟是 A的编码还是B的ASCII 的开始。所以我们还得标识出编码的长度 A1 B3 C3 D2,即01000001 00000000 0 01000010 00000011 100 01000011 00000011 101 01000100
15、 00000010 11A长度 0B长度 3C长度 3D长度 2这样的确是区别开了,没有歧义,可是编码变长了,我们可以规定叶子是按顺序排列的,此时编码就变短了,即:00000000 0 00000011 100 00000011 101 00000010 11A 的长度B 的长度C 的长度D 的长度事实上最大一共有 256 个叶子,计算机依次读 256 次就可以了。这样索引占用的长度一般是 512K 左右。不过一旦一个文件只有 5 片叶子,也得有13256 个字节来标识编码长度,这在压缩只有几个字节的小文件时,反而文件会扩大几十倍。C.写入树结构法如果我们知道原文件的哈夫曼树的结构,也自然就可
16、获知每个叶子的编码,那么,把这棵树的结构作为索引存入文件也可以,如果树可大可小,索引的长度也会可大可小,解决了 B 方法索引长度始终大于 256 字节的问题。如上图,如果非叶子节点为#,这个树的结构编码1就是”#A.#B.C.D.”而且哈夫曼树有一个性质,如果一个节点有左孩子,就一定有右孩子,如果没有左孩子,也必然没有右孩子。由此没有必要再用点号来表示叶子了,因而树结构简化成”#A#B#CD”,再用 1 表示叶子,0 表示非叶子,则为”01001011”,这就是它的存储实现。如下:00000100 01000001 01000010 01000011 01000100 01001011有 4
17、个节点ABCD树结构对于一棵完整的有 256 个叶子的 haffman 树,大约需要 320 字节就可以存储它了。比前面的方法节省了 38%。当然,要使用这种方法,就必须在压缩时用一个递归函数来遍历这棵树以获得树结构编码。D.关于索引的解码AB 两种建索引的方法都很方便于索引的解码,但空间占用大后者灵活性差,而若使用 C 方法,则索引的解码也成了问题。换句话说,我们得由“01001011”来还原出一棵 haffman 树。本系统是这样做的,首先得把树结构编码从文件中读到一个数组中,把1胡学钢 王浩 软件系列课程实践教程 第 49 页“扩展二叉树”14叶子编号读到另一个数组中,然后由这两个数组用
18、递归的方法造出树。然后由这棵树再求出每个叶子的编码。当然这一步可以略去了,因为解压缩采用寻径法,不需要再求每个叶子的具体编码了。E.相关细节问题为了给正文部分解码代码方便并显示解码进度,本系统在压缩的文件开头的四个字节记录了原文件的长度。索引中,节点的顺序和结构树的顺序必须相同,例如都采用先序,中序或后续,本系统采用先序。索引的编码和解码都用到了递归,而递归的参数都相当多而且很多是数组,为了节省空间,要运用引用操作。4.哈夫曼树显示算法A.简介在本系统的 windows 版本的程序中,有显示哈夫曼树的功能,这涉及到了计算机做图以及一些具体的算法。B.节点的布局一棵树在描绘之前必须要计算好每个节
19、点的坐标,否则漫无目的地从头节点分左右画下去就很可能造成节点位置的重合。Y 轴的坐标比较好控制,因为它有节点的深度决定了。X 轴呢?本系统利用中序遍历 haffman 树,对叶子节点 X 坐标递增的方法来确定的。例如左树,中序依次遍历了 ABCD,于是 ABCD 的横坐标就是 1,2,3,4。而非叶子节点的横坐标取左右孩子的横坐标的平均值,显然这是一个递归算法。15C.具体的描绘知道每个节点的坐标后,就可以开始描绘了,画圆与直线的函数都有了,因而遍历所有的节点也就可以把整个树给画出来了。D.细节问题计算坐标和描绘节点都是递归方法,因为程序的主体就是二个递归程序。由于节点众多,整个树画出来需要非
20、常宽的幅面,大约个象素的宽度。在 windows98 系统中不支持如此大的幅面,而在 window2000 和windowsXP 中支持,因而本系统作图功能不能在 win98 下体现甚至出现异常而终止了整个压缩程序。因而作图这一部分得使用 try/catch1这样的异常处理机制以确保压缩程序在各个系统的稳定运行。画出来的图比较大,一个屏幕显示不下,而仅使用滚动条又比较麻烦,因而本系统采用了“手抓”功能,可以用鼠标拖动画面。5.文件比对算法本系统具有文件比对功能,它的算法如下:首先,如果两个文件长度不相等,显然文件不相等,无须进一步比较了。怎么知道指定的文件的长度呢?如果把文件读一遍当然可以知道
21、它的长度,但这样太消耗时间。可以利用库的 filelength 函数来直接求得文件长度。1任常锐 黎涛C+builder 高级编程296 页16如果两个文件长度相同,则可以正式比对。同样为了加快速度,在此也用了全部变量的缓冲器。文件 A 可以用M 的读入缓冲器,文件 B 可以用M的写出缓冲器。如果均相同,则文件相等。然后一一对比,一旦出现不相同,则停止比对,输出“不相等”,程序返回。至此,整个算法的描述都已经叙述完了,本系统采用的算法均为以上各部分的最优算法,因而程序的结构比较复杂。四 用户使用说明(略)五 测试分析(一)各种类型文件的压缩率测试文件 1(英文文本 1)文件 2(英文文本 2)
22、文本文件(txt)文件 3(中文文本 1)文件 4(中文文本 2)文件 5(混合文本)Word文档文件大小7746565194289772110421167979261879压缩后515663511622169566088132181134172压缩率%66575399765259857869512317文件 1(英文文本 1)(doc)文件 2(英文文本 2)文件 3(中文文本 1)文件 4(中文文本 2)文件 5(混合文本)文件 1(无图片)146432506882867283763243175045391658321996801029123328035840030827813721843
23、02149420866355215662781078093083615090649675307637344111055614115156669154521952862833954075526317567453497400145934973626083526277607125740166747068550646435448919229706117716074969317网页文件(htm,mht)文件 2(有图片)文件 3(有图片)幻灯片文件文件 1(ppt)电子表格文件(xls)文件 2文件 1文件 2文件 1(16 色文件)位图文件(bmp)文件 2(256 色文件)文件 3(24 位真彩)文
24、件 1可执行文件(exe)文件 2文件 3(二)速度测试为避免偶然因素,本项测试选取一个600M 左右(621889873 byte)的虚拟光驱文件,压缩三次,取平均值。第一次压缩时间110.297s压缩速率5506Kb/s解压缩时间145.359s解压缩速率4178Kb/s18第二次第三次平均值107.859s120.359s112.838s5631Kb/s5046Kb/s5382Kb/s5382Kb/s150.344s141.313s145.611s4039Kb/s4298Kb/s4171Kb/s4171Kb/s以上测试环境:CPU:celeron1.7GHz,内存:DDR256M,硬盘:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 哈夫曼树 文件 压缩 解压 程序 示例 文档
限制150内