当前位置:文档之家› 数据结构课程设计报告

数据结构课程设计报告

《数据结构与算法》课程设计报告学号:班级序号:姓名:指导教师:成绩:中国地质大学信息工程学院地理信息系统系2011年12 月1.需求规格说明【问题描述】利用哈夫曼编码进行对已有文件进行重新编码可以大大提高减小文件大小,减少存储空间。

但是,这要求在首先对一个现有文件进行编码行成新的文件,也就是压缩。

在文件使用时,再对压缩文件进行解压缩,也就是译码,复原原有文件。

试为完成此功能,写一个压缩/解压缩软件。

【基本要求】一个完整的系统应具有以下功能:(1)压缩准备。

读取指定被压缩文件,对文件进行分析,建立哈夫曼树,并给出分析结果(包括数据集大小,每个数据的权值,压缩前后文件的大小),在屏幕上输出。

(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> 读文件,逐个读取字符,统计频率2> 建立哈夫曼树3> 获得哈弗曼编码2、压缩过程:1> 建立一个新文件,将储存权值和字符的对象数组取存储在文件头2> 从头连续读取源文件中的每个字符,对于所读取的每一个字符到统计表中查找相应的哈弗曼编码,每8位转换成一个ascii码,转换成字符存入新文件,不足八位的继续读取下一个字符,即:字符->哈弗曼编码->8位的二进制->ASCII码->字符,直到文件结束3> 统计文件大小,利用改变流指针的位置指向文件头和尾统计字符个数来统计文件大小3、解压过程:1> 将存于文件头的权值和字符的对象数组取出3> 重新建立哈夫曼树2> 建立一个新文件,从源文件中读取一个字符->ASCII码->8位的二进制->遍历树,遇到叶子节点则将节点中存的字符写入新文件,再读取下一个字符,直到文件结束(2)设计表示:统计频率:建立哈夫曼树:GetFrequency() GetTree()Judge(int c) HuffmanTree(charnode *ch, int n)MakeTree(const T& element Initialize(T a[], BinaryTree<T>& left, BinaryTree<T>& right) int size, int ArraySize)获得哈弗曼编码:压缩过程:Coding() StoreIntoNew ()GetIndex(char) GetIndex(char) EachStore(int ,ofstream &) ChangeInto(int a[])解压过程:Uncompress()Restore(BinaryTree<char>& bc Rechange(BinaryTree<char>& bc,int a,ofstream &output) ,int a,ofstream &output)(3)详细设计表示:class Compress{friend BinaryTree<char> HuffmanTree(charnode *,int n);//构造霍夫曼树public:Compress();void GetFrequency();bool Judge(int c);//判断是否c是否被读过int GetIndex(char c);//获取对应字符的索引void GetTree();//建立哈夫曼树void Coding();//获得哈弗曼编码int ChangeInto(int a[]);//将每位转化成一个intvoid EachStore(int a,ofstream &output);void StoreIntoNew();//将对应ASCII码存入新文件void Restore(BinaryTree<char>& bc,int a,ofstream &output);//遍历树直到叶节点时,解码,存入新文件void Rechange(BinaryTree<char>& bc,int a,ofstream &output);//将十进制转化为二进制,解码void Uncompress();//解压private:char *filepath;//源文件路径char *filepath2;//压缩后文件的路径char *filepath3;//解压后文件路径charnode ch[257];int count;//调试后发现为:字符的种数-1BinaryTree<char> bt;//建立的哈弗曼树BinaryTreeNode<char> *tnode;//解压时遍历树时需要的void coding(BinaryTreeNode<char> *t,Chain<int>& A);int bit[8];//存八位以供转化为ASCII码int bitcount;int total;//编码总长度int currentcount;//解压时纪录当前已解压的编码长度};字符类class charnode{public:charnode(){weigh=0;codelen=0;asciikey=0;}char character;//字符int asciikey;//字符对应的ASCII码int weigh;//权重int code[100];//存储哈弗曼编码int codelen;};3.编码1、用eof()作为循环控制条件时,总是会重复读取最后一个字符。

解决方法将while(input.eof()){...}改为while(true){input.get(temp);if(input.eof())break;...}2、在获得字符的哈弗曼编码,本来觉得就是一个前序遍历,但要编码与遍历时刻同步却是个难点,最后决定用链表代替栈,并且调整了顺序,首先判断是否需要导出编码,便于递归3、在压缩的时候首先想到要怎么解压,所以将解压所需的信息储存到文件头,却不能直接储已经存建立好的哈夫曼树,最后想到将建树所需的信息储存起来在解压前再建立一个相同的树4、对于源文件末尾编码不满8位字符的处理,将缺位数补零,在解压时仅遍历到总编码位数(已保存在文件头)5、在解压时,挨个读取压缩文件的字符时会有负数,第一次并没有考虑到,将十进制转化为二进制时得到的都是0,后面调试时才发现这个问题,于是在转化前首先判断正负,若是负数则加上2566、关于封装读取的字符类,前前后后改了很多次,最开始是结构体,结果不会调用,后面改成类,对于他的私有数据增增减减好几次4.程序及算法分析初始界面压缩功能压缩后生成的文件解压功能解压后的文件退出程序5.小结6.附录统计频率while(true){input.get(temp);//get(char& ch)是从流中读取一个字符,结果保存在引用ch 中,如果到文件尾,返回空字符。

if(input.eof())break;if(!Judge((int)temp))//Judge()判断该字符是否出现过{ch[count].asciikey=(int)temp;ch[count].character=temp;ch[count].weigh++;count++; }}input.close();}获得哈弗曼编码void Compress::coding(BinaryTreeNode<char> *t,Chain<int>& A){if(t->LeftChild==0 && t->RightChild==0)//当到达叶节点,然后读取相应队列中的代码存放到该编码{int index=GetIndex(t->data);A.GetCode(ch[index]);//存储哈弗曼编码A.Delete(A.Length());}else {if(t->LeftChild ){A.Insert(A.Length(),0);//向左走记为0coding(t->LeftChild,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);//读入的源文件cout<<"输入压缩后的文件路径输入文件名.Haf:"<<endl;cin>>filepath2;ofstream output;output.open(filepath2,ios::binary | ios::out);if(!output)//顺利打开否{cout <<"打开错误!"<<endl;}bt.count=count-1;//记录下字符的种数,以便解压bt.total =0;for(int i=1;i<=count-1;i++)//计算字符的总数bt.total+=ch[i].codelen*ch[i].weigh;ckcount=bt.total%8; //解压的时候发现必须记录下不满位的个数output.write((char *)&bt,sizeof(bt));//将构造好的哈弗曼树存储于文件中StoredNode snode[257];for(int i=1;i<count;i++)//为了节约空间只存取需要的信息{snode[i].character=ch[i].character ;snode[i].weigh =ch[i].weigh ;}output.write ((char*)&snode,sizeof(snode));output.flush();//刷新缓冲,并将缓存中的内容写入文件char temp;int i,j;input.get(temp);while(!input.eof())//读取每一个字符,直到文件末尾{j=GetIndex(temp);for(i=0;i<=ch[j].codelen-1;i++)//逐个bit送入{EachStore(ch[j].code[i],output);//每八位一输出}input.get(temp);}if(bitcount!=0)//处理不足位数的编码{for(int i=bitcount;i<8;i++)bit[i]=0;int n=ChangeInto(bit);output<<(char)n;output.flush();}input.close ();output.close ();ifstream file(filepath, ios::in|ios::binary);long l1,m1;l1=file.tellg();file.seekg(0,ios::end);m1=file.tellg();cout<<"压缩前文件:"<<(m1-11)/1024<<"KB"<<endl; file.close ();file.open(filepath2,ios::binary | ios::in);long l2,m2;l2=file.tellg();file.seekg(0, ios::end);m2 = file.tellg();cout<<"压缩后文件:"<<(m2-12)/1024<<"KB"<<endl;file.close ();cout<<"压缩完毕"<<endl;}解压过程void Compress::Uncompress()//解压{cout<<"输入要解压的文件路径,输入文件名:"<<endl;cin>>filepath2;fstream input;input.open(filepath2,ios::binary | ios::in);if(!input){cout <<"打开错误!"<<endl;}cout<<"请输入解压后文件路径: "<<endl;cin>>filepath3;ofstream output;output.open(filepath3,ios::app);//ios::app—所有输出附加在文件末尾while(!output){cout <<"打开错误!"<<endl;cout<<"请重新输入解压后文件的路径"<<endl;cin>>filepath3;}BinaryTree<char> bc;input.read((char *)&bc,sizeof(bc)); //读取哈夫曼树StoredNode snode[256];input.read((char *)&snode,sizeof(snode));int tempbc1,tempbc2;tempbc1=bc.count ;tempbc2=ckcount ;total=bc.total ;bc=HuffmanTree(snode,bc.count);//重新建树bc.count=tempbc1;ckcount=tempbc2;tnode=bc.root;char temcha;int intcha;input.get(temcha);while(!input.eof()){intcha=(int)temcha;if(intcha<0)intcha+=256;Rechange(bc,intcha,output);input.get(temcha);}input.close();output.close();cout<<"解压完成"<<endl;}。

相关主题