霍夫曼编码对英文文本地压缩和解压缩.docx
《霍夫曼编码对英文文本地压缩和解压缩.docx》由会员分享,可在线阅读,更多相关《霍夫曼编码对英文文本地压缩和解压缩.docx(13页珍藏版)》请在冰豆网上搜索。
霍夫曼编码对英文文本地压缩和解压缩
Huffman编码对英文文本的压缩和解压缩
中国地质大学计算机学院信息安全专业
信息论实验报告
#include
#include
#include
structhead{
unsignedcharb;//记录字符在数组中的位置
longcount;//字符出现频率(权值)
longparent,lch,rch;//定义哈夫曼树指针变量
charbits[256];//定义存储哈夫曼编码的数组
}header[512],tmp;
voidcompress()
{
charfilename[255],outputfile[255],buf[512];
unsignedcharc;
longn,m,i,j,f;//作计数或暂时存储数据用
longmin1,pt1,flength=0,length1,length2;//记录最小结点、文件长度
doublediv;//计算压缩比用
FILE*ifp,*ofp;//分别为输入、输出文件指针
printf("\t请您输入需要压缩的文件(需要路径):
");
gets(filename);
ifp=fopen(filename,"rb");
if(ifp==NULL){
printf("\n\t文件打开失败!
\n");
system("pause");
return;
}
printf("\t请您输入压缩后的文件名(如无路径则默认为桌面文件):
");
gets(outputfile);
ofp=fopen(outputfile,"wb");
if(ofp==NULL){
printf("\n\t压缩文件失败!
\n");
system("pause");
return;
}
flength=0;
while(!
feof(ifp)){
fread(&c,1,1,ifp);
header[c].count++;//字符重复出现频率+1
flength++;//字符出现原文件长度+1
}
flength--;
length1=flength;//原文件长度用作求压缩率的分母
header[c].count--;
for(i=0;i<512;i++){
if(header[i].count!
=0)
header[i].b=(unsignedchar)i;
/*将每个哈夫曼码值及其对应的ASCII码
存放在一维数组header[i]中,且编码表
中的下标和ASCII码满足顺序存放关系*/
else
header[i].b=0;
header[i].parent=-1;header[i].lch=header[i].rch=-1;//对结点进行初始化
}
for(i=0;i<256;i++){//按出现权值从大到小排序
for(j=i+1;j<256;j++){
if(header[i].counttmp=header[i];
header[i]=header[j];
header[j]=tmp;
}
}
}
for(i=0;i<256;i++)//找到第一个空的header结点
if(header[i].count==0)
break;
n=i;
m=2*n-1;
for(i=n;imin1=999999999;//预设的最大权值,即结点出现的最大次数
for(j=0;j
if(header[j].parent!
=-1)
continue;/*parent!
=-1说明该结点已存在哈夫曼
树中,跳出循环重新选择新结点*/
if(min1>header[j].count){
pt1=j;
min1=header[j].count;
continue;
}
}
header[i].count=header[pt1].count;
header[pt1].parent=i;
header[i].lch=pt1;
min1=999999999;
for(j=0;j
if(header[j].parent!
=-1)
continue;
if(min1>header[j].count){
pt1=j;
min1=header[j].count;
continue;
}
}
header[i].count+=header[pt1].count;
header[i].rch=pt1;
header[pt1].parent=i;//哈夫曼无重复前缀编码
}
for(i=0;if=i;
header[i].bits[0]=0;//根结点编码0
while(header[f].parent!
=-1){
j=f;
f=header[f].parent;
if(header[f].lch==j){//置左分支编码0
j=strlen(header[i].bits);
memmove(header[i].bits+1,header[i].bits,j+1);
//依次存储连接"0""1"编码,此处语句为网络借鉴
header[i].bits[0]='0';
}
else{//置右分支编码1
j=strlen(header[i].bits);
memmove(header[i].bits+1,header[i].bits,j+1);
header[i].bits[0]='1';
}
}
}
fseek(ifp,0,SEEK_SET);//从文件开始位置向前移动0字节,即定位到文件开始位置
fwrite(&flength,sizeof(int),1,ofp);/*用来将数据写入文件流中,参数flength
指向欲写入的数据地址,总共写入的字符数
以参数size*int来决定,返回实际写入的int数目*/
fseek(ofp,8,SEEK_SET);
buf[0]=0;//定义缓冲区,它的二进制表示00000000
f=0;
pt1=8;/*假设原文件第一个字符是"A",8位2进制为01000001,
编码后为0110识别编码第一个'0',那么将其左移一位,
看起来没什么变化。
下一个是'1',应该|1,结果00000001
同理4位都做完,应该是00000110,由于字节中的8位并没有
全部用完,继续读下一个字符,根据编码表继续拼完剩下
4位,如果字符的编码不足4位,还要继续读一个字符,如果
字符编码超过4位,那么把剩下的位信息拼接到一个新的字节里*/
while(!
feof(ifp)){
c=fgetc(ifp);
f++;
for(i=0;iif(c==header[i].b)
break;
}
strcat(buf,header[i].bits);
j=strlen(buf);
c=0;
while(j>=8){
for(i=0;i<8;i++){
if(buf[i]=='1')
c=(c<<1)|1;//添加最后一位为1
else
c=c<<1;//添加最后一位为0
}
fwrite(&c,1,1,ofp);
pt1++;
strcpy(buf,buf+8);
j=strlen(buf);
}
if(f==flength)
break;
}
if(j>0){//最后不满八位的buf处理
strcat(buf,"00000000");//buf后加八位0
for(i=0;i<8;i++){//逐位输入八位前的二进制符
if(buf[i]=='1')
c=(c<<1)|1;
else
c=c<<1;
}
fwrite(&c,1,1,ofp);
pt1++;
}
fseek(ofp,4,SEEK_SET);//指针回到输出文件头部后面四位
fwrite(&pt1,sizeof(long),1,ofp);//pt1统计了输出文件中的字符个数,包括了最后的'/0'
fseek(ofp,pt1,SEEK_SET);
fwrite(&n,sizeof(long),1,ofp);//n统计了权值不为0的header[]数量
for(i=0;ifwrite(&(header[i].b),1,1,ofp);//依次写入每个叶子结点的b、长度和内容
c=strlen(header[i].bits);
fwrite(&c,1,1,ofp);
j=strlen(header[i].bits);
if(j%8!
=0){//若存储的位数不是8的倍数,则补0
for(f=j%8;f<8;f++)
strcat(header[i].bits,"0");
}
while(header[i].bits[0]!
=0){/*字符的有效存储不超过8位,则对
有效位数左移实现两字符编码的连接,
可理解为前缀编码也被压缩过*/
c=0;
for(j=0;j<8;j++){
if(header[i].bits[j]=='1')
c=(c<<1)|1;
else
c=c<<1;
}
strcpy(header[i].bits,header[i].bits+8);
fwrite(&c,1,1,ofp);//以上与上面连接字符一段可相同理解
}
}
length2=pt1--;
div=((double)length1-(double)length2)/(double)length1;//计算文件的压缩率
fclose(ifp);
fclose(ofp);
printf("\n\t压缩文件成功!
\n");
printf("\t压缩率为%f%%\n\n",div*100);
return;
}
voiduncompress(){
charfilename[255],outputfile[255],buf[255],bx[255];
unsignedcharc;
longi,j,m,n,f,p,l;
longflength;
FILE*ifp,*ofp;
printf("\t请您输入需要解压缩的文件(如无路径则默认为桌面文件):
");
gets(filename);
ifp=fopen(filename,"rb");
if(ifp==NULL){
printf("\n\t文件打开失败!
\n");
system("pause");
return;
}
printf("\t请您输入解压缩后的文件名:
");
gets(outputfile);
ofp=fopen(outputfile,"wb");
if(ofp==NULL){
printf("\n\t解压缩文件失败!
\n");
system("pause");
return;
}
fread(&flength,sizeof(long),1,ifp);//读入文件长度flength
fread(&f,sizeof(long),1,ifp);//读入header数组的存储地址
fseek(ifp,f,SEEK_SET);
fread(&n,sizeof(long),1,ifp);//读入header数组中元素的个数
for(i=0;ifread(&header[i].b,1,1,ifp);
fread(&c,1,1,ifp);
p=(long)c;
header[i].count=p;
header[i].bits[0]=0;
if(p%8>0)
m=p/8+1;
else
m=p/8;
for(j=0;jfread(&c,1,1,ifp);
f=c;
itoa(f,buf,2);/*此处借鉴网络程序,包括itoa()函数
itoa()函数的作用为,把int型的f
化为二进制数,再变成char型存入buf*/
f=strlen(buf);
for(l=8;l>f;l--){//在单字节内对相应位置补0
strcat(header[i].bits,"0");
}
strcat(header[i].bits,buf);
}
header[i].bits[p]=0;
}
for(i=0;ifor(j=i+1;jif(strlen(header[i].bits)>strlen(header[j].bits)){
tmp=header[i];
header[i]=header[j];
header[j]=tmp;
}
}
}
p=strlen(header[n-1].bits);
fseek(ifp,8,SEEK_SET);
m=0;
bx[0]=0;
while
(1){//对文件其余部分,即真正的文件部分解压缩
while(strlen(bx)<(unsignedint)p){
fread(&c,1,1,ifp);
f=c;
itoa(f,buf,2);
f=strlen(buf);
for(l=8;l>f;l--){
strcat(bx,"0");
}
strcat(bx,buf);
}
for(i=0;iif(memcmp(header[i].bits,bx,header[i].count)==0)/*此函数也为网络借鉴,memcmp函数此处的作用
是比较bx的相应位是否与header[i].bits相同,
若前header[i].count均相同,则返回0*/
break;
}
strcpy(bx,bx+header[i].count);
c=header[i].b;
fwrite(&c,1,1,ofp);
m++;//m用来统计解压缩后文件的长度
if(m==flength)//检验是否与源文件长度匹配
break;
}
fclose(ifp);
fclose(ofp);
printf("\n\t解压缩文件成功!
\n");
if(m==flength)
printf("\t解压缩文件与原文件相同!
\n\n");
else
printf("\t解压缩文件与原文件不同!
\n\n");
return;
}
intmain(){
intc;
while
(1){
printf("\tHuffman前缀编码压缩&解压缩BYPB09000816俞映洲\n");
printf("\t1.压缩2.解压缩0.退出\n");
do{//对用户输入进行容错处理
printf("\n\t*请选择相应功能编号(0-2):
");
c=getch();
printf("%c\n",c);
if(c!
='0'&&c!
='1'&&c!
='2'){
printf("\t请检查您的输入在0~2之间!
请再输入一遍!
\n");
}
}while(c!
='0'&&c!
='1'&&c!
='2');
if(c=='1')//调用压缩子函数
compress();
elseif(c=='2')
uncompress();//调用解压缩子函数
else{
exit(0);
}
return0;
}