基于赫夫曼编码的文本压缩程序.docx
- 文档编号:7157145
- 上传时间:2023-01-21
- 格式:DOCX
- 页数:21
- 大小:22.32KB
基于赫夫曼编码的文本压缩程序.docx
《基于赫夫曼编码的文本压缩程序.docx》由会员分享,可在线阅读,更多相关《基于赫夫曼编码的文本压缩程序.docx(21页珍藏版)》请在冰豆网上搜索。
基于赫夫曼编码的文本压缩程序
基于赫夫曼编码的文本压缩程序
一、目的及意义
通过课程设计的综合训练,旨在帮助学生进一步系统的掌握数据结构这门课的主要内容,并进一步培养学生分析问题和解决问题的能力,主要体现在能够让学生针对实际问题有效地组织数据,选择合适的数据结构,并进行正确和高效地算法设计,并用程序实现算法。
该课的课程设计是一个良好的程序设计技能训练的过程使学生能够:
1.了解并掌握数据结构与算法的设计方法,具备初步的独立分析和设计能力。
2.初步掌握软件开发过程的问题分析、系统设计、程序编码、测试等基本技能和方法。
3.提高综合运用所学的理论知识和方法独立分析和解决问题的能力。
4.训练用系统的观点和软件开发一般规范进行软件开发,培养计算机科学与技术专业学生所具备的科学的工作方法和作风。
二、程序功能描述
程序实现的功能:
对文本文件进行压缩以及对压缩的文本文件进行解压缩。
程序的实现的理论依据是赫夫曼编码。
赫夫曼编码是一种无损的压缩算法,一般用来压缩文本和程序文件。
赫夫曼压缩属于可变代码长度算法一族。
意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。
因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。
程序由三个文件组成:
头文件CourseDesign.h、函数实现文件CourseDesign.cpp、测试文件Test.cpp。
在CourseDesign.h中声明数据的存储结构以及程序所需要的处理函数;CourseDesign.cpp文件实现在CourseDesign.h中声明的函数;Test.cpp负责对所实现的函数进行调用测试,确定是否满足程序设计要求。
利用赫夫曼编码实现对文本的压缩的过程大致为:
打开要压缩的文本文件,读取文件中的字符,统计文件中不同字符出现的频率,建立赫夫曼树,通过赫夫曼树对出现的互不相同的字符进行编码,建立编码表,接着将将赫夫曼树(即解码表)写入压缩文件中。
再次重新读取文件中的字符,对每个字符通过查阅编码表确定对应的编码,将该字符的赫夫曼编码写入压缩文件。
对压缩文件的解压过程为:
打开压缩文件,读取压缩文件解码表部分,读取压缩文件的编码数据,将压缩数据通过解码表进行解码,将解码出的字符写入解码文件中。
程序执行后,用户按照程序的提示选择相应的功能选项。
当用户选择压缩功能,此时程序要求用户输入要压缩的文本文件的路径,用户输入完成后。
程序检查文件是否能够建立。
检查完成后,程序将文件从硬盘读入内存。
接着程序将统计不同字符出现的频率以及建立编码表的初步结构。
例如当文件中存有如下表所示字符。
表1文件字符属性表
字符第一字节机内码/ASCII第二字节机内码权重
的18119620a9709
把17620914
表1772375
班1762241
补1781852
百17621417
防18319212
飞1832019
博17816913
包1762522
才1781976
方1831898
拜1762213A6503
份1832215
必1772165
英文字符在计算机中是以标准ASCII码进行表示,一个英文字符占一个字节空间,编码范围为0~127;汉字在计算机中是以GB2312编码进行表示。
每个汉字占两个字节存储空间,汉字的存储使用机内码,各字节的机内码编码范围为160~254。
现在需要考虑使用怎样的数据结构来存放这些字符,如果采用如下简单的数据结构存放:
typedefstruct
{
chardata[3];//存放字符
intinternal_code1;//存放第一字节的机内码/ASCII码
intinternal_code2;//存放第二字节的机内码,英文默认为0intweight;//存放字符的权重
char*code;//字符的赫夫曼编码
}CodeList,*CodePList;
分析所要处理的字符数据会发现:
许多的字符的第一字节的机内码相同,如"防"、"飞"、"方"、"份",第一字节机内码都是183。
这是因为汉字是通过划分区号和位号来表示的,所有汉字被划分成了94个区,94个位,所以当汉字属于同一个区,那么它的第一字节机内码就会相同。
如果采用如上的数据结构建立的线性表来存放处理字符,就会存在大量数据冗余。
在这种情况下,就有必要为特定的数据设计合适的数据结构。
通过分析,采用如下数据结构:
typedefstruct
{
charinternal_code;//存放第二字节机内码
char*code;//存放字符的赫夫曼编码
}InternalCode;
typedefstruct
{
intcount;//已编码字符数
charinternal_code;//存放第一字节机内码
InternalCode*internal_code_address;//第二字节机内码及字符的
}HuffmanCode,*HuffmanPCode;//赫夫曼编码的地址
该结构的优点:
当汉字的第一字节机内码相同,则该第一字节机内码只会被存储一次,从而消除汉字第一字节机内码存储的冗余,而且可以方便的使用折半查找快速检索编码表来确定字符的赫夫曼编码。
采用该数据结构对表1数据进行表示如图1。
图1编码表HC的存储结构
这种数据结构形式上类似于图的邻接表结构,功能上类似于哈希表的链地址法。
但邻接表的表结点采用链式存储,而图1的表结点和头结点都采用线性表储存。
图1中编码表HC的内码1是纵向非递减排列,内码2是横向非递减排列。
HC[i].count–HC[i–1].count等于HC[i]实际存储的字符数量。
例如,HC[3]中字符数为7,HC[2]中字符数为2,则HC[3]存放了5个字符,这5个字符拥有相同的第一字节机内码176。
程序执行压缩操作详细过程:
当程序从文件中读取一个字符后,通过字符的编码范围分析该字符是属于ASCII还是GB2312,若是ASCII编码,增加编码表HC纵向表长,将该字符的ASCII码按非递减次序插入到内码1处,并将当前位置的字符数加1,并置内码2默认为0;如果是汉字,首先通过折半查找算法纵向查找编码表HC的内码1成员,若当前汉字第一字节机内码已经出现过,则折半查找返回该机内码1在HC表中的位置,增加当前位置的横向表长,将汉字的第二字节机内码按非递减次序插入当前位置的内码2处。
否则将汉字的第一字节机内码按非递减次序插入HC表的内码1区域,第二字节机内码直接插入内码2处。
在读取文件的同时记录文件中各字符出现的频率,当编码表HC表构建完成,此时w={3,9,14,3,1,2,17,5,5,13,2,6,20,9,8,5,12}。
依次从w中选择权重最小并且双亲为0的两个结点,根据这两个结点生成新的结点,新结点的权重为这两个最小结点的和,新结点的左右子树为这两个结点在w中的位置。
根据表1数据构建赫夫曼树如图2所示。
赫夫曼树存储结构的初始状态如图3(a),终结状态如图3(b)。
图2根据表1构造的赫夫曼树
图3(a)HT初始状态图3(b)HT终止状态
根据生成的赫夫曼树对HC表中的字符进行编码,编码的方法:
从该叶子到根逆向求该字符的编码。
例如图2中"把"的权值为14,对应的编码为:
"000"。
将得到的赫夫曼编码写入HC[i].internal_code_address[j].code指向的区域。
当字符编码完成之后,打开压缩文件,将赫夫曼树HT中除权重以外的数据(解码无需权重信息)写入压缩文件中,作为下一次解压缩的解码表。
再次打开要压缩的文本文件,读取文件中的字符,根据编码的范围确定该字符是ASCII还是GB2312,如果ASCII则调用折半查找函数,在编码表HC中进行纵向查找,查找此ASCII出现的位置p1,该字符的编码为HC[p1].internal_code_address[1].code;如果字符是汉字,则调用折半查找先纵向查找该汉字的第一字节机内码在HC中的位置p1,然后从HC[p1].internal_code_address开始横向查找该汉字的第二字节机内码的位置p2,这样就得到了该汉字的赫夫曼编码为HC[p1].internal_code_address[p2].code因为赫夫曼编码在HC表中是以字符串形式存放(因为计算机的基本储单位是字节,如果以位存放,需要另设一个空间来表示未编码的位空间大小)。
所以需要将字符串"0101"转换为二进制0101写入文件。
因为每个赫夫曼编码的长度是不一样的,假设某字符的赫夫曼长度为4,则将该编码写入一个字节后,还剩余4个位,则下一次可以继续从第5个位开始写入,当所有字符的编码都写入完毕后,最后一个字节并不一定会用完,所以需要附设一个字节来记录最后一个字符编码实际写入的编码位数。
编码文件的结构如下图所示:
图4压缩文件存储结构
程序解压文件:
打开压缩文件,取出压缩文件的解码表长度N,根据N读取N条解码表记录,重建解码表HT,然后读取压缩数据DATA,解码的过程是从根出发,按DATA数据的0或1确定找左子树还是右子树,直至叶子结点,便求得了DATA相应的字符。
将字符写入文件,直至所有DATA数据处理完毕,整个解压过程结束。
三、程序源代码
1.头文件CourseDesign.h
#ifndef_COURSEDESIGN_H_
#define_COURSEDESIGN_H_
//---Huffman树存储结构
typedefstruct
{
charch[3];
unsignedintweight;
unsignedintparent,lchild,rchild;
}HTNode,*HuffmanTree;
//--Huffman编码表存储结构
typedefstruct
{
charinternal_code;
char*code;
}InternalCode;
typedefstruct
{
intcount;
charinternal_code;
InternalCode*internal_code_address;
}HuffmanCode,*HuffmanPCode;
//---解码表存储结构
typedefstruct
{
charch[3];
unsignedintlchild,rchild;
}DecodeList,*DecodePList;
//--辅助数组,置/取一个字节的指定位
conststaticunsignedcharmask[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
templateclassTstaticintxj_Search_Bin(intkey,TL,intlow,inthigh);
templateclassTstaticvoidxj_InsertSort(TL,intstart,intend);
voidxj_Select(constHuffmanTreeHT,intn,int&s1,int&s2);
voidxj_Statistics(HuffmanPCode&HC,intinternal_code1,intinternal_code2,int(*FrequencyMeter)[255],int&n);
boolxj_Init(char*filename,HuffmanPCode&HC,int*&w,int&n);
voidxj_CreateHuffmanTree(HuffmanTree&HT,constHuffmanPCodeHC,constint*w,intn);
voidxj_HuffmanCoding(constHuffmanTreeHT,HuffmanPCodeHC,intn);
boolxj_Compress(char*ifilename,char*ofilename,constHuffmanPCodeHC,constHuffmanTreeHT,intn);
boolxj_DeCompress(char*ifilename,char*ofilename);
voidxj_Interface();
#endif2.函数实现文件CourseDesign.cpp
#include"CourseDesign.h"
#includeiostream
#includefstream
#includeiomanip
#includemalloc.h
#includestring.husingnamespacestd;
//---折半查找--
templateclassT
intxj_Search_Bin(intkey,TL,intlow,inthigh)
{
intmid=0;
intinternal_code;
while(low=high)
{
mid=(low+high)/2;
internal_code=int(L[mid].internal_code&0xFF);
if(key==internal_code)
{
returnmid;
}
elseif(internal_codekey)
{
high=mid-1;
}
else
{
low=mid+1;
}
}
return0;
}
//--对HC表的字符域做插入非递减排序---
templateclassT
voidxj_InsertSort(TL,intstart,intend)
{
inti;
L[0]=L[end];
i=end-1;
while(i=start&&int(L[i].internal_code&0xFF)int(L[0].internal_code&0xFF))
{
L[i+1]=L[i];
i--;
}
L[i+1]=L[0];
}
//---寻找权重最小的两个结点--
voidxj_Select(constHuffmanTreeHT,intn,int&s1,int&s2)
{
inti=0;
s1=s2=0;
for(i=1;i=n;++i)
{
if(HT[i].parent==0)
{
if(s1==0)
{
s1=i;
}
elseif(s2==0)
{
s2=i;
}
elseif(HT[i].weightHT[s1].weight||HT[i].weightHT[s2].weight)
{
s1=HT[s1].weightHT[s2].weight?
s1:
s2;
s2=i;
}
}
}
}
//--构建HC.internal_code以及HC.internal_code_address结构---
voidxj_Statistics(HuffmanPCode&HC,intinternal_code1,intinternal_code2,int(*FrequencyMeter)[255],int&n)
{
intposition;
if(internal_code1128)
{
if(FrequencyMeter[internal_code1][0]==0)
{
++n;
HC=(HuffmanPCode)realloc(HC,(n+1)*sizeof(HuffmanCode));
HC[n].internal_code=internal_code1;
HC[n].count=1;
HC[n].internal_code_address=(InternalCode*)malloc(2*sizeof(InternalCode));
HC[n].internal_code_address[1].internal_code=0;//0号单元未用
xj_InsertSort(HC,1,n);
}
++FrequencyMeter[internal_code1][0];
}
else
{
if(FrequencyMeter[internal_code1][internal_code2]==0)
{
position=xj_Search_Bin(internal_code1,HC,1,n);
if(position!
=0)
{
++HC[position].count;
HC[position].internal_code_address=(InternalCode*)realloc(HC[position].internal_code_address,(HC[position].count+1)*sizeof(InternalCode));
HC[position].internal_code_address[HC[position].count].internal_code=internal_code2;
xj_InsertSort(HC[position].internal_code_address,1,HC[position].count);
}
else
{
++n;
HC=(HuffmanPCode)realloc(HC,(n+1)*sizeof(HuffmanCode));
HC[n].internal_code=internal_code1;
HC[n].count=1;
HC[n].internal_code_address=(InternalCode*)malloc(2*sizeof(InternalCode));
HC[n].internal_code_address[1].internal_code=internal_code2;
xj_InsertSort(HC,1,n);
}
}
++FrequencyMeter[internal_code1][internal_code2];
}
}
//--统计不同字符出现的频率以及构建HC的机内码成员结构---
boolxj_Init(char*filename,HuffmanPCode&HC,int*&w,int&n)
{
ifstreamifs(filename);
inti=0,j=0;
intFrequencyMeter[255][255]={0};
charch1,ch2;
n=0;
HC=NULL;
w=NULL;
if(ifs.fail())
{
cout"can'topenfile!
"endl;
returnfalse;
}
while((ch1=ifs.get())!
=EOF)
{
if(int(ch1&0xFF)128)
{
ch2=ifs.get();
}
else
{
ch2=0;
}
xj_Statistics(HC,int(ch1&0xFF),int(ch2&0xFF),FrequencyMeter,n);
}
HC[0].count=0;
for(i=2;i=n;++i)HC[i].count+=HC[i-1].count;
w=(int*)malloc(HC[n].count*sizeof(int));
for(i=1;i=n;++i)
{
for(j=HC[i-1].count;jHC[i].count;++j)
{
w[j]=FrequencyMeter[int(HC[i].internal_code&0xFF)][int(HC[i].internal_code_address[j-HC[i-1].count+1].internal_code&0xFF)];
}
}
ifs.close();
returntrue;
}
//--构造赫夫曼树HT---
voidxj_CreateHuffmanTree(HuffmanTree&HT,constHuffmanPCodeHC,constint*w,intn)
{
inti=0,j=0;
intm=0,s1=0,s2=0;
if(HC[n].count=1)return;
m=2*HC[n].count-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(i=1;i=n;++i)
{
for(j=HC[i-1].count+1;j=HC[i].count;++j,++w)
{
HT[j].ch[0]=HC[i].internal_code;
HT[j].ch[1]=HC[i].internal_code_address[j-HC[i-1].count].internal_code;
HT[j].ch[2]='[message]';
HT[j].weight=*w;
HT[j].lchild=HT[j].rchild=HT[j].parent=0;
}
}
for(i=HC[n].count+1;i=m;++i)
{
*HT[i].ch=0;
HT[i].weight=HT[i].lchild=HT[i].rchild=HT[i].parent=0;
}
for(i=HC[n].count+1;i=m;++i)
{
xj_Select(HT,i-1,s1,s2);
HT[s1].parent=i;HT[s2].parent=i;
HT[i].lchild=s1;HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
}
//--建立编码表HC---
voidxj_HuffmanCoding(constHuffmanTreeHT,HuffmanPCodeHC,intn)
{
intstart=0,c=0,f=0;
inti=0,k=1,r=1;;
char*cd=NULL;
cd=(char*)malloc(HC[n].count*sizeof(char));
cd[HC[n].count-1]='[message]';
for(i=1;i=HC[n].count;++i)
{
start=HC[n].count-1;
for(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)
{
if(HT[f].lchild==c)
{
cd[--start]='0';
}
else
{
cd[--start]='1';
}
}
if(kHC[r].count-HC[r-1].count)
{
k=1;
++r;
}
HC[r].internal_code_address[k].code=(char*)malloc((HC[n].count-start)*sizeof(char));
strcpy(HC[r].internal_code_address[k].code,&cd[start]);
++k;
}
free(cd);
}
//---压缩文件--
boolxj_Compress(char*ifilename,char*ofilename,constHuffmanPCodeHC,constHuffmanTreeHT,intn)
{
ifstreamifs(ifilename);
ofstreamofs(ofilename,ios:
binary);
intbit_size=0;
intposi
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 赫夫曼 编码 文本 压缩 程序