1、 数据结构与算法课程设计报告学 号: 班级序号: 姓 名: 指导教师: 成 绩: 中国地质大学信息工程学院地理信息系统系2011 年 12 月1需求规格说明【问题描述】利用哈夫曼编码进行对已有文件进行重新编码可以大大提高减小文件大小,减少存储空间。但是,这要求在首先对一个现有文件进行编码行成新的文件,也就是压缩。在文件使用时,再对压缩文件进行解压缩,也就是译码,复原原有文件。试为完成此功能,写一个压缩/解压缩软件。【基本要求】一个完整的系统应具有以下功能:(1)压缩准备。读取指定被压缩文件,对文件进行分析,建立哈夫曼树,并给出分析结果(包括数据集大小,每个数据的权值,压缩前后文件的大小) ,在
2、屏幕上输出。(2)压缩。利用已建好的哈夫曼树,对文件进行编码,并将哈夫曼编码及文件编码后的数据一起写入文件中,形成压缩文件(*.Haf)。(3)解压缩。打开已有压缩文件(*.Haf),读取其中的哈夫曼编码,构建哈夫曼树,读取其中的数据,进行译码后,写入文件,完成解压缩。(4)程序使用命令行方式运行压缩命令 :SZip A Test.Haf 1.doc解压缩命令:SZip X Test.Haf 2.doc 或 SZip X Test.Haf 用户输入的命令不正确时,给出提示。(5)使用面向对象的思想编程,压缩/解压缩、哈夫曼构建功能分别构建类实现。2总体分析与设计(1)设计思想:1、压缩准备:1
3、 读文件,逐个读取字符,统计频率2 建立哈夫曼树3 获得哈弗曼编码2、压缩过程:1 建立一个新文件,将储存权值和字符的对象数组取存储在文件头2 从头连续读取源文件中的每个字符,对于所读取的每一个字符到统计表中查找相应的哈弗曼编码,每 8 位转换成一个 ascii 码,转换成字符存入新文件,不足八位的继续读取下一个字符,即:字符-哈弗曼编码-8 位的二进制-ASCII码- 字符,直到文件结束3 统计文件大小,利用改变流指针的位置指向文件头和尾统计字符个数来统计文件大小3、解压过程:1 将存于文件头的权值和字符的对象数组取出3 重新建立哈夫曼树2 建立一个新文件,从源文件中读取一个字符-ASCII
4、 码-8 位的二进制-遍历树,遇到叶子节点则将节点中存的字符写入新文件,再读取下一个字符,直到文件结束(2)设计表示:统计频率: 建立哈夫曼树: GetFrequency() GetTree()Judge(int c) HuffmanTree(charnode *ch, int n)MakeTree(const T/构造霍夫曼树public:Compress();void GetFrequency();bool Judge(int c);/判断是否c是否被读过int GetIndex(char c);/获取对应字符的索引void GetTree();/建立哈夫曼树void Coding();/
5、获得哈弗曼编码int ChangeInto(int a);/将每位转化成一个intvoid EachStore(int a,ofstream void StoreIntoNew();/将对应ASCII码存入新文件void Restore(BinaryTree/遍历树直到叶节点时,解码,存入新文件void Rechange(BinaryTree/将十进制转化为二进制,解码void Uncompress();/解压private:char *filepath;/源文件路径char *filepath2;/压缩后文件的路径char *filepath3;/解压后文件路径charnode ch257;
6、int count;/调试后发现为:字符的种数-1BinaryTree bt;/建立的哈弗曼树BinaryTreeNode *tnode;/解压时遍历树时需要的void coding(BinaryTreeNode *t,Chainint bit8;/存八位以供转化为ASCII码int bitcount;int total;/编码总长度int currentcount;/解压时纪录当前已解压的编码长度;字符类class charnodepublic:charnode()weigh=0;codelen=0;asciikey=0;char character;/字符int asciikey;/字符对
7、应的ASCII 码int weigh;/权重int code100;/存储哈弗曼编码int codelen;3编码1、用 eof()作为循环控制条件时,总是会重复读取最后一个字符。解决方法将while(input.eof().改为 while(true)input.get(temp);if(input.eof()break;. 2、在获得字符的哈弗曼编码,本来觉得就是一个前序遍历,但要编码与遍历时刻同步却是个难点,最后决定用链表代替栈,并且调整了顺序,首先判断是否需要导出编码,便于递归3、在压缩的时候首先想到要怎么解压,所以将解压所需的信息储存到文件头,却不能直接储已经存建立好的哈夫曼树,最后
8、想到将建树所需的信息储存起来在解压前再建立一个相同的树4、对于源文件末尾编码不满 8 位字符的处理,将缺位数补零,在解压时仅遍历到总编码位数(已保存在文件头)5、在解压时,挨个读取压缩文件的字符时会有负数,第一次并没有考虑到,将十进制转化为二进制时得到的都是 0,后面调试时才发现这个问题,于是在转化前首先判断正负,若是负数则加上 2566、关于封装读取的字符类,前前后后改了很多次,最开始是结构体,结果不会调用,后面改成类,对于他的私有数据增增减减好几次4程序及算法分析初始界面压缩功能压缩后生成的文件解压功能解压后的文件退出程序5小结6附录统计频率while(true) input.get(te
9、mp);/get(charif(!Judge(int)temp)/Judge()判断该字符是否出现过chcount.asciikey=(int)temp;chcount.character=temp;chcount.weigh+;count+; input.close();获得哈弗曼编码void Compress:coding(BinaryTreeNode *t,ChainA.GetCode(chindex);/存储哈弗曼编码A.Delete(A.Length();else if(t-LeftChild )A.Insert(A.Length(),0);/向左走记为0coding(t-LeftC
10、hild,A);if(t-RightChild )A.Insert(A.Length(),1);/向右走记为1coding(t-RightChild,A);A.Delete(A.Length();压缩过程void Compress:StoreIntoNew()/将对应ASCII码存入新文件ifstream input;input.open(filepath);/读入的源文件coutfilepath2;ofstream output; output.open(filepath2,ios:binary | ios:out);if(!output)/顺利打开否cout filepath2;fstre
11、am input;input.open(filepath2,ios:binary | ios:in);if(!input) cout filepath3;ofstream output;output.open(filepath3,ios:app);/ios:app所有输出附加在文件末尾while(!output)cout filepath3;BinaryTree bc;input.read(char *) /读取哈夫曼树StoredNode snode256;input.read(char *)int tempbc1,tempbc2;tempbc1=bc.count ;tempbc2=bc.l
12、ackcount ;total=bc.total ;bc=HuffmanTree(snode,bc.count);/重新建树bc.count=tempbc1;bc.lackcount=tempbc2;tnode=bc.root;char temcha;int intcha;input.get(temcha);while(!input.eof() intcha=(int)temcha;if(intcha0)intcha+=256;Rechange(bc,intcha,output);input.get(temcha); input.close();output.close(); cout“解压完成“ endl;