哈弗曼编码译码器.docx
《哈弗曼编码译码器.docx》由会员分享,可在线阅读,更多相关《哈弗曼编码译码器.docx(13页珍藏版)》请在冰豆网上搜索。
哈弗曼编码译码器
1需求分析
1、利用哈夫曼编码进行信息通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
对于双工信道(既可以双向传输信息的信道),每端都需要一个完整的编/译码系统。
2、本次课程设计就是要设计一个哈夫曼编码/译码系统,对一个文本文件中的字符进行哈夫曼编码,生成编码文件(压缩文件,后缀.cod),反过来,可将一个压缩文件译码还原为一个文件(.txt)
2题目要求
1.输入一个待压缩的文件名,统计文本文件中的各字符的个数作为权值,生成哈夫曼树。
2.将文本文件利用哈夫曼树进行编码,生成压缩文件(.cod)
3.输入一个待解压文件名称,并利用相应的哈夫曼树将编码序列译码。
4.显示指定的压缩文件和文本文件。
3运行环境(软、硬件环境)
windowsXP/windows2000
4开发工具和编程语言
开发工具:
MicrosoftVisualC++;
编程语言:
C++;
5详细设计
(1)首先,对一些基本的基本输入输出函数,字符串函数,动态存储分配等函数进行定义:
#include//包含基本输入输出函数
#include//包含字符串函数
#include//包含exit()及动态存储分配等函数
#include//C++
#include
#include
对输入的字符数进行定义:
(用N代表能够编码的最大字符数)
例如:
设输入编码的最大的字符数为100,则具体的操作如下:
#defineN100
(2)然后,定义字符和其存储信息,定义哈夫曼树各结点的存储信息,具体的操作如下:
structcharacter{//定义字符
charcha;//用来保存各字符信息
intcount;//存放字符的权值
}R[N];
structHuffmanNode{//定义哈夫曼树各结点
intweight;//存放结点的权值
intparent;//记录结点父亲位置
intlchild,rchild;//分别存放该结点的左、右孩子的所在单元的编号
}HTNode,*Node;
intNum;//记录所要编译的文本中的字符的个数
char*Info;//存放结点的字符信息
chartoken[10];//用来存储原文件名
charfile_name[10],file_name1[10];//用来存储目标文件名
(3)统计文本文件中的各字符的个数作为权值,生成哈夫曼树。
在操作中要首先对哈夫曼树的初始化:
用函数voidInitialization()来完成;具体的操作如下:
voidInitialization()
{
inti,j,pos1,pos2,max1,max2;
Node=newHuffmanNode[2*Num-1];//Num权值对应的哈夫曼树中的结点总数为2*Num-1个
Info=newchar[2*Num-1];
for(i=0;iInfo[i]=R[i].cha;//传送字符信息
Node[i].weight=R[i].count;//传送权值
Node[i].parent=-1;//为根结点
Node[i].lchild=-1;//无左孩子
Node[i].rchild=-1;//无右孩子
}
for(i=Num;i<2*Num-1;i++)//表示需做Num-1次合并
{pos1=-1;
pos2=-1;//分别用来存放当前最小值和次小值的所在单元编号
max1=32767;//32767为整型数的最大值
max2=32767;//分别用来存放当前找到的最小值和次小值
for(j=0;j
if(Node[j].parent==-1)//是否为根结点
if(Node[j].weightmax2=max1;//原最小值变为次小值
max1=Node[j].weight;//存放最小值
pos2=pos1;//修改次小值所在单元编号
pos1=j;//修改最小值所在单元编号
}
else
if(Node[j].weightmax2=Node[j].weight;//存放次小值
pos2=j;//修改次小值所在的单元编号
}
Node[pos1].parent=i;//修改父亲位置
Node[pos2].parent=i;
Node[i].lchild=pos1;//修改儿子位置
Node[i].rchild=pos2;
Node[i].parent=-1;Node[i].weight=Node[pos1].weight+Node[pos2].weight;
}
(4)将文本文件利用哈夫曼树进行编码,生成压缩文件(.cod)完成该要求的函数操作和算法如下:
voidEncoder()//编码
{
if(Node==NULL){//哈夫曼树不在内存,从文件hfmTree中读入
ifstreamfip;//以二进制方式打开hfmTree.dat文件
fip.open("hfmTree.dat",ios:
:
in);//ios:
:
in
if(fip.fail()){//文件打开失败
cout<<"文件打开失败!
\n";
return;//结束本函数
}
fip.read((char*)&Num,sizeof(Num));//读取叶子数
Info=newchar[Num];
Node=newHuffmanNode[2*Num-1];
for(inti=0;ifip.read((char*)&Info[i],sizeof(Info[i]));
for(i=0;i<2*Num-1;i++)//读取结点信息
fip.read((char*)&Node[i],sizeof(Node[i]));
}
char*Tree;//用于存储需编码内容
inti=0;
ifstreamfip1(token);
if(fip1.fail())//文件不存在
{cout<<"文件打开失败!
\n";
return;//结束本函数
}
charch;
intk=0;
while(fip1.get(ch)){
k++;}//计算文件中代码长度
fip1.close();
Tree=newchar[k+1];
ifstreamfip2(token);
k=0;
while(fip2.get(ch))
{
Tree[k]=ch;//读取文件内容,并存到Tree中
k++;
}
fip2.close();
cout<<"需编码内容为:
";
for(i=0;icout<cout<<"\n";
cout<<"请输入存储编码的文件名(包括后缀名(.cod)):
";
cin>>file_name1;
ofstreamfop(file_name1,ios:
:
trunc);//存储编码后的代码,并覆盖原文件
i=0;k=0;
char*code;
code=newchar[Num];//为所产生编码分配容量为Num的存储空间
cout<<"文件进行编码后为:
";
while(Tree[k]!
='#')//对每一个字符编码
{intj,start=0;
for(i=0;iif(Info[i]==Tree[k])//求出该文字所在单元的编号
break;
j=i;
while(Node[j].parent!
=-1)//结点j非树根
{j=Node[j].parent;//非结点j的双亲结点
if(Node[j].lchild==i)//是左子树,则生成代码0
code[start++]='0';
else//是右子树,则生成代码1
code[start++]='1';
i=j;
}code[start]='\0';//置串结束符
for(i=0;ij=code[i];
code[i]=code[start-i-1];
code[start-i-1]=j;
}
i=0;
while(code[i]!
='\0'){//存储代码
cout<fop<i++;
}//cout<<"";
k++;
}
fop.close();
cout<<"\n编码成功!
且已存到文件"<\n\n";
}
(5)输入一个待解压文件名称,并利用相应的哈夫曼树将编码序列译码。
完成译码所用到的函数操作和算法如下:
voidDecoder()//译码
{
inti=0,k=0;
intj=Num*2-1-1;//表示从根结点开始往下搜索
char*BitStr;
cout<<"请输入需要进行译码的文件名(包括后缀名(.cod)):
";
cin>>file_name1;
ifstreamfip1(file_name1);//利用已建好的哈夫曼树将文件中的代码进行译码
if(fip1.fail())//文件打开失败,还未编码
{cout<<"请先编码!
\n";
return;
}
charch;
while(fip1.get(ch)){
k++;//计算文件中代码长度
}
fip1.close();
BitStr=newchar[k+1];
ifstreamfip2(file_name1);
k=0;
while(fip2.get(ch))
{
BitStr[k]=ch;//读取文件内容
k++;
}
fip2.close();
BitStr[k]='\0';//结束标志符
if(Node==NULL)//还未建哈夫曼树
{cout<<"请先编码!
\n";
return;
}
cout<<"请输入储存译码后的文件名(包括后缀名(.txt)):
";
cin>>file_name;
ofstreamfop(file_name);//将字符形式的编码文件写入文件中
if(fop.fail())//文件不存在
{cout<<"文件打开失败!
\n";
return;//结束本函数
}cout<<"经译码,原内容为:
";
while(BitStr[i]!
='\0')
{if(BitStr[i]=='0')
j=Node[j].lchild;//往左走
else
j=Node[j].rchild;//往右走
if(Node[j].rchild==-1)//到达叶子结点
{cout<fop<j=Num*2-1-1;//表示重新从根结点开始往下搜索
}
i++;
}
fop.close();
cout<<"\n译码成功!
且已存到文件"<\n\n";
}
6调试分析
在程序没有语法和逻辑错误的时候,进行运行的时候,一直不能够输出结果,经过认真的查找才发现,在程序中要求,建立的文本中的内容要以“#”结尾,而在现实建立的文本中的内容最后忘了以#结尾。
修改后程序能够正确的运行。
在程序调试成功后,建立一个命名为:
“a.txt”的文本,在文本中放入“yanglinsen#”一串字符作为测试数据,然后运行程序,经过运行测试程序能够输出结果,且正确的统计出字符的个数及权值:
字符:
yanglise
权值:
11311111
而且,能构成产生其相应的哈夫曼树,
然后,进行编码,在输入存储编码的文件名:
“a.cod”后,文件进行编码后为:
11101111100000010101001111010编码成功,而且已存到文件“a.cod”中!
经验证确实生成a.cod文件。
最后,进行译码,先输入要进行译码的文件名“a.cod”,然后再命名一个为“ase.txt”的文件来存储译码后的内容,经译码,原内容为:
yanglinsen译码成功,且已存到文件ase.txt中!
经验证确实生成ase.txt文件。
经过上个数据的测试,该程序能够正确地完成题目所要求的条件,但是,再输入一组数据:
while(ch!
='{or(i=Num;i<2*Num-1;i++)
t=0;ad{pos1=-1;if(ch==R[n].cha){a
R[n]d.count++;t+f+;}gj
if(t==0)i++;R[i].cha=chR[i].count=1;
ch=fgetc(fp)klxc}
则在运行中出现错误,能够统计出其字符及权值,但不能进行编码和译码,经过思考发现在程序中,在定义输入能够编码的最大的字符数
#defineN10
而在上一组数据中,字符的个数超过了10,所以不能够进行操作,出现运行错误,把程序中的“N”的定义值改成100,再进行运行测试,则能够输出正确的结果。
7测试结果
测试数据
(1)
首先,建立一个文本,命名为“yang.txt”在文本中放入
Whereareyoucomefrom?
IcomefromChina!
Wemaytalkofbeautifulthings,
butbeautyitselfisabstract!
#
做为测试数据。
运行结果:
(1)字符个数及其权值:
(2)哈夫曼树的生成:
(3)将文本内容利用哈夫曼树进行编码,生成压缩文件(.cod)
(4)输入一个待解压文件名称,并利用相应的哈夫曼树将编码序列译码。
测试数据2:
建立一个文本“lin.txt”在这个文本里放入“河南工业大学#”作为测试数据。
运行结果:
(1)字符个数及其权值及哈夫曼树的生成:
(2)将文本内容利用哈夫曼树进行编码,生成压缩文件(.cod)及输入一个待解压文件名称,并利用相应的哈夫曼树将编码序列译码。
8参考文献
[1]严蔚敏,数据结构(c语言版),北京:
清华大学出版社,2002,144~148
[2]WilliamFordWilliamTopp,数据结构C++语言描述,北京:
清华大学出版社,1998.11,421~451
[3]谭浩强,C程序设计(第二版),清华大学出版社,1999.12
心得体会
1、经过这次课程设计实验,更好的掌握了数据结构中的哈夫曼树,并对哈夫曼树有了深一步的了解,认识到了哈夫曼编码和译码在现实生活中的巨大作用。
2、掌握了构建生成哈夫曼树,统计其结点和权值的函数和算法,也掌握了哈夫曼编码的函数voidEncoder()和译码voidDecoder(),对哈夫曼编码/译码的算法也有了进一步的了解,也更熟悉了文件的操作。
3、通过这次实验认识到,对一些关键的算法理解分析透彻,这样在程序调试的时候可以剩不少时间。
4、在完成实验的过程中认识到认真仔细的重要性,在实验过程中,由于粗心大意,没有认真的读程序,出现了一些错误,浪费了不少的时间。
5、经过这次课程设计,对我的读,写程序的能力和发现问题和解决问题的能力都有一定的提高。