Huffman压缩实验报告Beta14.docx
《Huffman压缩实验报告Beta14.docx》由会员分享,可在线阅读,更多相关《Huffman压缩实验报告Beta14.docx(18页珍藏版)》请在冰豆网上搜索。
![Huffman压缩实验报告Beta14.docx](https://file1.bdocx.com/fileroot1/2023-2/2/b0b5444e-0b35-42b5-92e1-a6aeb3b8fa82/b0b5444e-0b35-42b5-92e1-a6aeb3b8fa821.gif)
Huffman压缩实验报告Beta14
用Huffman编码实现文档压缩
实验目的
1.了解有关文件的基本概念和操作
2.掌握哈夫曼树的概念及其构造方法
3.运用哈夫曼树及其编码
实验环境
Windows7ProfessionalServicePark1,64bit
MicrosoftVisualStudio2010
实验内容
输入需要压缩的文本文件名,对该文件中的各个字符出现的频度进行统计,然后构建Huffman编码树及Huffman编码表。
读入源文件,将源文件翻译成Huffman编码文件,输出到文件1.huf,最后读入1.huf文件,将Huffman编码文件翻译成文本文件,输出到_1.txt。
要求计算压缩比输出:
压缩比是编码后文件字符数除以编码前文件中含有字符数。
设计概要
压缩部分
1.构造哈夫曼树,对其进行前缀编码
(1)扫描待压缩文件,得出各字符出现频率。
(2)根据给定的n个权值{W1,W2,...Wn}构成n棵二叉树的集合F={T1,T2,…,Tn},每棵二叉树Ti中只有一个带权为Wi的根节点,其左右子树均空。
(3)在F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且值新的二叉树的根节点的权值为其左右子树上根节点的全值之和。
(4)在F中删除这两棵树。
同时将新得到的二叉树加入F中。
重置
(2)和(3),直到F只含一棵树为止。
这棵树便是哈夫曼树。
2.由Huffman树得到各字符前缀编码。
3.根据前缀编码,对文件中各个字符进行编码,并按每八位一次写入压缩文件。
4.处理剩余不到八位部分,写入压缩文件。
5.将前缀编码及相关信息写入压缩文件。
6.关闭指针,完成压缩。
计算压缩率。
解压部分
1.读入压缩文件长度和源文件长度、读入前缀编码。
2.对文件中各字符解码,写入解压文件。
源码:
#include
#include
#include
#include
structhufflist
{
unsignedcharasc;
longweight;
charcode;
intpar,lch,rch;
hufflist()
{asc=-1;weight=0;par=lch=rch=-1;};//初始化
};
//缓冲队列
typedefstructtemp
{
intn,fr,be;
charctemp[1200];
temp()
{n=fr=be=0;};
intpush(charch)
{
if(n>1024)
return1;
ctemp[be]=ch;
be++;
be=be%1024;
n++;
return0;
};
charpop(void)
{
charch;
if(n==0)
return0;
ch=ctemp[fr];
fr++;
fr=fr%1024;
n--;
returnch;
};
}temp;
voidmain()
{
FILE*fp1,*fp2,*fp3;
intch=0;
floatra;
charinfile[256];
charoutfile[256];
floatcompress(FILE*infile,FILE*outfile);
voiduncompress(FILE*infile,FILE*outfile);
while(ch!
=3)
{
system("cls");
printf("==================文档压缩&解压工具==================\n\n\n");
printf("①>>压缩文档\n\n②>>解压文档\n\n③>>退出程序\n\n\n");
printf("请输入要进行的操作:
\n");
scanf("%d",&ch);
switch(ch)
{
case1:
{
printf("请输入原文件路径:
");
getchar();
gets(infile);
if((fp1=fopen(infile,"r"))==NULL)
{
printf("ERROR!
文件打开失败!
\n");
getchar();
break;
}
printf("请输入输出文件路径:
");
gets(outfile);
if((fp2=fopen(outfile,"wb"))==NULL)
{
printf("ERROR!
文件打开失败!
\n");
getchar();
break;
}
if(strcmp(infile,outfile)==0)
{
printf("ERROR!
文件路径冲突!
\n");
getchar();
break;
}
ra=compress(fp1,fp2);
printf("文件压缩完成!
\n");
printf("压缩比率为%.4f%%\n",ra*100);
getchar();
break;
}
case2:
{
printf("请输入原文件路径:
");
getchar();
gets(infile);
if((fp2=fopen(infile,"rb"))==NULL)
{
printf("ERROR!
文件打开失败!
\n");
getchar();
break;
}
printf("请输入输出文件路径:
");
gets(outfile);
if((fp3=fopen(outfile,"w"))==NULL)
{
printf("ERROR!
文件打开失败!
\n");
getchar();
break;
}
if(strcmp(infile,outfile)==0)
{
printf("ERROR!
文件路径冲突!
\n");
getchar();
break;
}
uncompress(fp2,fp3);
printf("文件解压完毕!
\n");
getchar();
break;
}
default:
{
ch=3;
printf("程序关闭!
\n");
break;
}
}
}
return;
}
floatcompress(FILE*infile,FILE*outfile)
{
structhufflistHT[513];
char**HC;
intROOT,i,j,k,s,node_count=0,lnode_count,lnode_count_t,ad1,ad2;
//node_count总结点个数lnode_count没有父节点的节点个数
longweight2=0;
floatsv,a,b;
unsignedcharch;
tempmytemp;
char*ct=NULL;
//先统计输入文本infile中字符信息
ch=fgetc(infile);
while(!
feof(infile))
{
for(i=0;i<=513;i++)
{
if(HT[i].asc==ch)
{
HT[i].weight++;
break;
}
if(HT[i].weight==0)
{
HT[i].asc=ch;
HT[i].weight++;
node_count++;
break;
}
}
ch=fgetc(infile);
}
lnode_count=node_count;
lnode_count_t=lnode_count;
while(lnode_count_t>1)
{
//找到权值最小节点
for(i=0;i{
if(HT[i].par==-1)
break;
}
ad1=i;
for(j=i+1;j{
if(HT[j].par==-1&&HT[j].weightad1=j;
}
HT[ad1].par=1;
//找到权值次小节点
for(i=0;i<=node_count;i++)
{
if(HT[i].par==-1)
break;
}
ad2=i;
for(j=i+1;j{
if(HT[j].par==-1&&HT[j].weightad2=j;
}
HT[ad2].par=1;
HT[ad1].par=HT[ad2].par=node_count;
HT[node_count].lch=ad1;HT[node_count].rch=ad2;
HT[ad1].code=0;HT[ad2].code=1;
HT[node_count].weight=HT[ad1].weight+HT[ad2].weight;
node_count++;
lnode_count_t--;
}
ROOT=node_count-1;
fwrite(HT,sizeof(structhufflist),513,outfile);
//建立编码表
HC=(char**)malloc(256*sizeof(char*));
ct=(char*)malloc((lnode_count+1)*sizeof(char));
ct[lnode_count]=-1;
for(i=0;i<=255;i++)
{
HC[i]=NULL;
}
for(i=0;i{
for(k=i,j=lnode_count-1;HT[k].par!
=-1;k=HT[k].par,j--)
{
ct[j]=HT[k].code;
}
HC[HT[i].asc]=(char*)malloc(lnode_count*sizeof(char));
for(s=0,j++;ct[j]!
=-1;s++,j++)
{
HC[HT[i].asc][s]=ct[j];
}
HC[HT[i].asc][s]=-1;
}
rewind(infile);
//重编码
ch=fgetc(infile);
while(!
feof(infile))
{
for(i=0;HC[ch][i]!
=-1;i++)
{
mytemp.push(HC[ch][i]);
}
while(mytemp.n>=8)
{
ch=128*mytemp.pop()+64*mytemp.pop()+32*mytemp.pop()+16*mytemp.pop()+8*mytemp.pop()+4*mytemp.pop()+2*mytemp.pop()+mytemp.pop();
fwrite(&ch,1,1,outfile);
weight2++;
}
ch=fgetc(infile);
}
while(mytemp.n>0)
{
ch=128*mytemp.pop()+64*mytemp.pop()+32*mytemp.pop()+16*mytemp.pop()+8*mytemp.pop()+4*mytemp.pop()+2*mytemp.pop()+mytemp.pop();
fwrite(&ch,1,1,outfile);
weight2++;
}
fclose(infile);
fclose(outfile);
a=weight2+513*sizeof(structhufflist);
b=HT[ROOT].weight;
sv=a/b;
return(sv);
}
voiduncompress(FILE*infile,FILE*outfile)
{
structhufflistHT[513];
intROOT,i=0;
unsignedcharch;
tempmytemp;
fread(HT,sizeof(structhufflist),513,infile);
while(HT[i].par!
=-1)
{
i=HT[i].par;
}
ROOT=i;
fread(&ch,1,1,infile);
while(!
feof(infile))
{
while(mytemp.n<=768&&(!
feof(infile)))
{
for(i=0;i<=7;i++)
{
mytemp.push((ch<
}
fread(&ch,1,1,infile);
}
i=ROOT;
do
{
if(mytemp.pop())
i=HT[i].rch;
else
i=HT[i].lch;
}while(HT[i].lch!
=-1);
fputc(HT[i].asc,outfile);
}
while(mytemp.n>0)
{
i=ROOT;
do
{
if(mytemp.pop())
i=HT[i].rch;
else
i=HT[i].lch;
}while(HT[i].lch!
=-1&&mytemp.n>0);
fputc(HT[i].asc,outfile);
}
fclose(infile);
fclose(outfile);
return;
}
数据测试
文件:
D:
\1.txt(英文原著《飘》)大小:
2.25MB
程序运行界面:
压缩后实际文件大小对比:
解压后文件对比:
进一步测试了多个文本文件,程序运行正常,未发现错误。
容错性检查
输入不存在的或者输入输出冲突的文件路径则结果如下图,解压缩部分的结果类似。
实验小结
1.本程序的难点:
Huffman树的生成和压缩、解压缩时字符串的衔接。
2.程序较长,最初程序模块构架不合理导致程序调试失败、运行效率低。
3.程序中较低层的接触到了C语言环境下的文件的管理机制,通过在网络上查询重新认识了C环境下文件的管理机制。
4.MicrosoftVisualStudio2010提供了很多调试工具,合理使用这些调试工具可以较大的提高程序调试效率。