用赫夫曼编码完成文件压缩.docx
《用赫夫曼编码完成文件压缩.docx》由会员分享,可在线阅读,更多相关《用赫夫曼编码完成文件压缩.docx(20页珍藏版)》请在冰豆网上搜索。
用赫夫曼编码完成文件压缩
实验报告
题目:
用赫夫曼编码实现文件压缩
班级:
理科实验四班姓名:
王渭森
学号:
2015201992完成日期:
2016.6.10
1、需求分析
1.现实需求:
1)通信线路中实现信息的最大传送,利用变长编码的方式,可以有效充分利用带宽,实现信息传送前的压缩。
2).在文件保存时,利用哈夫曼编码的方式,压缩文件,可以实现硬盘存储最大的信息量。
2.程序执行的命令包括:
1)读取文件。
2)新建并写入文件。
3)将文件中的字符转换为哈夫曼编码。
4)将哈夫曼编码八位一组专户为十进制,在通过十进制的ASCII码转换为字符。
5)翻译过程现将字符转为01串,再将01串翻译为原来的文件。
3.测试数据
1)
2)
3)
4)
5)
新建一个文件并压缩。
2、概要设计
1.串的抽象数据类型定义为:
ADTString{
数据对象:
D={ai|ai∈CharacterSet,i=1,2,...,n,n>=0}
数据关系:
R1={|ai-1,ai∈D,i=1,2,...,n}
基本操作:
strcpy(String&S1,StringS2)
初始条件:
串S2存在。
操作结果:
由串S2复制得串S1.
strlen(SStringS)
初始条件:
串S存在。
操作结果:
返回S的元素个数,称为串的长度。
}//ADTString
2.二叉树的抽象数据类型定义为:
ADTBinaryTree{
数据对象D:
是具有相同特性的元素的集合。
数据关系R:
若D为空集,则称为空二叉树;
若D仅含一个数据元素,则R为空集,否则R = {H}, H是如下二元关系:
1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
2) 若D – {root} ≠ Φ,则存在D – {root} 的一个划分Dl Dr ,Dl∩Dr =Φ;
3) 若Dl≠Φ,则Dl 中存在惟一的数据元素x, ∈ H,且存在Dl 上的关系Hl < H;若Dr ≠ Φ,则Dr 中存在惟一的数据元素xr , ∈ H,且存在Dr 上的关系Hr < H;
4)(Dl,{ Hl、})是一棵符合本定义的二叉树,称为根root的左子树,(Dr,{ Hr、})是一棵符合本定义的二叉树,称为根root的右子树。
基本操作P:
InitBitree(&T);
操作结果:
构造空二叉树。
CreateBitree(&T);
初始条件:
二叉树存在。
操作结果:
按输入格式构造二叉树。
Value(T, e);
初始条件:
二叉树存在,e是T中某个结点。
操作结果:
返回结点e的值。
Assign(&T, &e, value);
初始条件:
二叉树存在,e是T中某个结点。
操作结果:
结点e赋值为value 。
Parent(T, e);
初始条件:
二叉树存在,e是T中某个结点。
操作结果:
若e是T的非根结点,则返回它的双亲,否则返回“空”。
LeftChild(T, e);
初始条件:
二叉树存在,e是T中某个结点。
操作结果:
返回e的左孩子。
若e无左孩子,则返回“空”。
RightChild(T, e);
初始条件:
二叉树存在,e是T中某个结点。
操作结果:
返回e的右孩子。
若e无右孩子,则返回“空”。
}ADTBinaryTree
3.赫夫曼树和赫夫曼编码的存储表示:
typedefstruct{
chardata;
intweight;
intparent,lchild,rchild;
}HTNode,*HuffmanTree;//动态分配数组存储赫夫曼树
typedefchar**HuffmanCode;//动态分配数组存储赫夫曼编码。
4.函数的调用关系图:
main()->CompressFile()->HuffmanCoding()->Select()
->WriteFile()->CoToCh()
->DeCompressFile()->ChToCo(CName);
->translation()
3、调试分析
1.文件中字符数目可能非常大,不能用一个整的数组来存,所以需要从文件中一个字符一个字符来读取处理。
2.为解决文件中字符出现的不确定性,用数组character[256]来记录相应ASCII的字符出现次数,统计完后,将出现次数非零的字符存在数组v[]中,并将它们的权值存在数组w[]中。
3.在将赫夫曼编码翻译为字符中,translation()中函数的变量ch,在运算中应该变它的对应的值,即为,传参应为char&ch,而不应为charch。
4.将哈夫曼八位一组转为十进制时,01串中个数不一定为8的倍数,先遍历文件,统计01串中元素个数,将该个数除以8的余数拿出来,放入压缩文件的第一位,在依次将等于余数个数的01字符直接放入压缩文件,之后的01串为8的整数倍。
5.读取压缩后的乱码是,可能读出负数,若读出负数,让这个负数加上256再转化为2进制的01串。
4、测试结果
1.
如图,4为余数。
压缩率:
125%
可见,当文件中元素个数小于8时,压缩文件反而会增大。
2.
压缩率:
7/30
3.
压缩率:
9/30。
4.
原文件大小为:
21.2KB
压缩文件大小为11.3KB
压缩率接近百分之五十。
总和2、3、4可见,可见,相同大小的文件,其中元素分布越集中,压缩率越大。
5.
五、附录
源程序文件清单:
#defineMAXWEIGHT2147483647
#defineMAXASCII255
#defineMAXNAME100
#include
#include
#include
#include
typedefstruct{
chardata;
intweight;
intparent,lchild,rchild;
}HTNode,*HuffmanTree;//动态分配数组存储赫夫曼树
typedefchar**HuffmanCode;//动态分配数组存储赫夫曼编码。
voidSelect(HuffmanTreeHT,intindex,int&s1,int&s2)
{//从序号为1~index的结点中选出两个最小的且没有双亲的结点。
intw1,w2,t,i;
s1=0;s2=0;w1=MAXWEIGHT;w2=MAXWEIGHT;
for(i=1;i<=index;i++)
{
if(HT[i].parent==0)
{
if(HT[i].weight{
s2=s1;w2=w1;
s1=i;w1=HT[i].weight;
}
elseif(HT[i].weight{
s2=i;w2=HT[i].weight;
}
}
}
}
intHuffmanCoding(HuffmanTree&HT,HuffmanCode&HC,char*v,int*w,intn)
{//v存放需要编码的n个字符,w存放对应的权值,
//构造赫夫曼树,并求出n个字符的赫夫曼编码。
if(n==0)
{//没有合法字符时
printf("没有需要编码的字符!
");
return-1;
}
if(n==1)
{//只有一个合法字符时
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
HC[1]=(char*)malloc(3*sizeof(char));
HC[1][0]=*v;
HC[1][1]='0';
HC[1][2]=0;
HT=(HuffmanTree)malloc(2*sizeof(HTNode));
HT[1].parent=0;
HT[1].lchild=0;
HT[1].rchild=0;
HT[1].weight=*w;
HT[1].data=*v;
return0;
}
ints1,s2,m,i;
m=2*n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
HuffmanTreep;
HT->weight=m;
for(p=HT+1,i=1;i<=n;i++,p++,w++,v++)
{
p->weight=*w;
p->lchild=0;
p->rchild=0;
p->parent=0;
p->data=*v;
}
for(;i<=m;i++,p++)
{
p->weight=0;
p->lchild=0;
p->rchild=0;
p->parent=0;
}
for(i=n+1;i<=m;i++)
{//在HT[1..i-1]选择parent为0而且weight最小的
//两个结点且weight最小的两个结点,其序号分别
//为s1和s2
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;
}
//---从叶子到根逆向求每个字符的霍夫曼编码---
char*cd;
intstart,c,f;
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
cd=(char*)malloc(n*sizeof(char));
cd[n-1]=0;
for(i=1;i<=n;i++)
{
start=n-1;
for(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)
if(HT[f].lchild==c)cd[--start]='0';
elsecd[--start]='1';
HC[i]=(char*)malloc((n-start+1)*sizeof(char));
strcpy(HC[i]+1,&cd[start]);
HC[i][0]=v[i-1-n];
}
free(cd);
return1;
}
voidtranslation(FILE*fp1,FILE*fp2,char&ch,HuffmanTreeHT,inti,intf)
{//逐字翻译压缩文件中的赫夫曼编码
if(HT[i].lchild==0&&HT[i].rchild==0)
{//若为叶子结点,则成功翻译一个字符,将它输入到解压文件中
fputc(HT[i].data,fp2);
if(f==0)ch=fgetc(fp1);
return;
}
if(ch=='0')
{//若为0,则探索左孩子
f=1;
ch=fgetc(fp1);
translation(fp1,fp2,ch,HT,HT[i].lchild,f);
}
elseif(ch=='1')
{//若为1,则探索左孩子
f=1;
ch=fgetc(fp1);
translation(fp1,fp2,ch,HT,HT[i].rchild,f);
}
}
voidCoToCh(charCName[])
{
FILE*fp1,*fp2;
charDName[MAXNAME]={0},ch;//存放压缩后的文件名
intBin[7]={0},asc;//八个01一组放入Bin串;二进制对应的ASCII值
inti,j,num=0,r;
printf("请输入你希望得到的压缩后的文件名:
");
gets(DName);
fp1=fopen(CName,"rb");
fp2=fopen(DName,"wb");
while(!
feof(fp1))//计算0,1的数量
{
ch=fgetc(fp1);
num++;
}
num--;
rewind(fp1);
r=num%8;//八个一组多余出来的01
fputc(r+'0',fp2);//将多出来的数量的值放在压缩文件第一位
for(j=1;j<=r;j++)
{//后面r位原封不动的将01复制进来
ch=fgetc(fp1);
fputc(ch,fp2);
}
while(!
feof(fp1))
{
i=0;asc=0;
memset(Bin,0,sizeof(Bin));
while(!
feof(fp1)&&i<=7)
{
ch=fgetc(fp1);
Bin[i]=ch-'0';
i++;
}
if(feof(fp1))break;
for(j=0;j<=i-1;j++)
asc=asc*2+Bin[j];
fputc(asc,fp2);
}
fclose(fp1);fclose(fp2);
printf("压缩后的文件名为:
");puts(DName);
}
voidWriteFile(HuffmanCodeHC,charFName[],intk,charv[])
{//将被压缩的文件内容写入另一个文件
FILE*fp1,*fp2;
charCName[MAXNAME]={0},ch;
strcpy(CName,"CP");
strcpy(CName+2,FName);//哈夫曼编码文件命名为"'CP'+原文件名"
fp1=fopen(FName,"rb");
fp2=fopen(CName,"wb");
while
(1)
{
inti;
ch=fgetc(fp1);
if(feof(fp1))break;
for(inti=0;i<=k-1;i++)
{
if(ch==v[i])
fputs(HC[i+1]+1,fp2);//将赫夫曼编码写入哈夫曼编码文件
}
}
fclose(fp1);fclose(fp2);
CoToCh(CName);
}
voidCompressFile(HuffmanTree&HT,HuffmanCode&HC,int&k)
{//读取文件中的字符,构造哈夫曼曼树,得到每个字符的哈夫曼编码
FILE*fp;
intflag;
charch,v[MAXASCII+1],FName[MAXNAME];//v存放出现的字符
intcharacter[MAXASCII+1]={0};//统计每种字符出现的次数
intw[MAXASCII+1];//w放字符的权值
printf("请键入要运行的操作编号:
\n1.压缩已有的文件\n2.建立新文件,输入内容并进行压缩\n");
scanf("%d",&flag);//选择操作
getchar();
if(flag==1)
{
printf("请键入需要压缩的文件名:
");
gets(FName);
fp=fopen(FName,"r");
}
elseif(flag==2)
{
printf("请键入需要压缩的文件名:
");
gets(FName);
printf("请键入文件内容:
\n");
fp=fopen(FName,"w");
while(ch!
='\n')
{
scanf("%c",&ch);
fputc(ch,fp);
}
fclose(fp);
fp=fopen(FName,"r");
}
else
{
printf("警告:
无此操作项!
");
return;
}
while
(1)
{//逐个读取文件中的字符,数组character对应字符位置上+1
inti;
ch=fgetc(fp);
if(feof(fp))break;
character[ch]++;
}
for(inti=0;i<=MAXASCII-1;i++)
{//若某字符在在文件中出现,则把它放入v,并把权值计入相同序号的w
if(character[i]!
=0)
{
v[k]=i;
w[k++]=character[i];
}
}
printf("文件中出现的字符及它们的权值:
\n");
for(inti=0;i<=k-1;i++)
printf("%c-%d;\n",v[i],w[i]);
HuffmanCoding(HT,HC,v,w,k);
fclose(fp);
printf("上述字符对应的哈夫曼编码:
\n");
for(inti=1;i<=k;i++)
puts(HC[i]);
WriteFile(HC,FName,k,v);
}
voidChToCo(charDName[],charCName[])
{
FILE*fp1,*fp2;
intr,i,j,Bin[7]={0},asc;
charch;
fp1=fopen(DName,"rb");
strcpy(CName,"RCP");
strcpy(CName+3,DName);
fp2=fopen(CName,"wb");
r=fgetc(fp1)-'0';
for(i=1;i<=r;i++)
{
ch=fgetc(fp1);
fputc(ch,fp2);
}
while
(1)
{
ch=fgetc(fp1);
if(feof(fp1))break;
asc=(int)ch;
if(asc<0)asc+=256;
for(i=7;i>=0;i--)
{
Bin[i]=asc%2;
asc=asc/2;
}
for(i=0;i<=7;i++)
fputc(Bin[i]+'0',fp2);
}
fclose(fp1);fclose(fp2);
}
voidDecompressFile(HuffmanTreeHT,intk)
{//解压函数
FILE*fp1,*fp2;
charDName[MAXNAME],FName[MAXNAME],CName[MAXNAME]={0},ch;
intr;
printf("请键入需要解压的文件名:
(注意:
文件中的字符权值需与上述相同!
)");
gets(DName);
printf("请输入解压后希望得到的文件名:
");
gets(FName);
printf("解压后的文件名为:
");puts(FName);
ChToCo(DName,CName);
fp1=fopen(CName,"rb");
fp2=fopen(FName,"wb");
ch=fgetc(fp1);
while(!
feof(fp1))
{//到压缩文件末尾结束
translation(fp1,fp2,ch,HT,2*k-1,0);
}
fclose(fp1);fclose(fp2);
}
intmain()
{
HuffmanTreeHT;
HuffmanCodeHC;
intk=0,flag;
CompressFile(HT,HC,k);
printf("请键入要运行的操作编号:
\n1.结束进程\n2.解压文件\n");
scanf("%d",&flag);
getchar();
if(flag==1){}
elseif(flag==2)
DecompressFile(HT,k);
else
printf("警告:
无此操作项!
");
return0;
}