赫夫曼编码解码
一、实验目的
掌握赫夫曼树和赫夫曼编码的基本思想和算法的程序实现。
二、 实验内容及要求
任务描述
实现文件中数据的加解密与压缩:将硬盘上的一个文本文件进行加密,比较加密文件和原始文件 的大小差别;对加密文件进行解密,比较原始文件和解码文件的内容是否一致。
程序源代码:
DS.h
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
HFTree.h
#include <DS.h>
typedef struct HTNode
{
int value;
int p,l,r;
} HTNode, *HuffTree;
typedef struct fact //因为不是每一篇文章中所有字符都会出现
{ //所以结构体数组存储数组下标真正对应的字符ch以及权值weight
char ch;
int weight;
}fact;
typedef char * * HuffCode; // 字符指针数组用于存储各个字符对应的编码
typedef char * CHAR;
void select(HuffTree HT,int n,int *s1,int *s2); //查找 HT 中未被使用的权值最小的连个点的下标
void HUFFTREE(HuffTree *HT,fact *ww,int n,HuffCode *HC); //建树函数,附带完成每一个字符对应的编码
void BecomeCode(HuffCode *HC1,int n,CHAR *Code1,char *Text,int *match); //由已知的各个字符的编码完成全文的编码
void Code_ToBe_Artical(CHAR Code,HuffTree HT,fact *Fact,int n); //由全文的编码,用已经建立的哈弗曼树完成解码
HFTree.c
#include <HFTree.h>
void select(HuffTree HT,int n,int *s3,int *s4)
{
int s1 = *s3,s2 = *s4;
s1=s2=0;
HT[0].value=0x7fffffff;
for(int i=1; i<=n; i++)
{
if(HT[i].p!=0)
continue;
if(HT[i].value<HT[s1].value)
{
s2=s1;
s1=i;
}
else if(HT[i].value<HT[s2].value)
s2=i;
}
*s3 = s1;
*s4 = s2;
}
void HUFFTREE(HuffTree *HT1,fact *ww,int n,HuffCode *HC1)
{
HuffTree HT;
HT = *HT1;
HuffCode HC;
HC = *HC1;
int m=n*2-1;
HT = (HuffTree)malloc((m+1)*sizeof(HTNode)); //分配 m+1 个内存,是因为要存储 m 个数据,但是要从 HT 数组下标 1 开始
int i,j,f;
fact *w=ww;
HuffTree p;
for(p =HT,p++,i=1; i<=n; i++,p++,w++) //对 HT (1~n)赋值语句
{
(*p).value=(*w).weight,(*p).p=0,(*p).l=0,(*p).r=0;
}
for(; i<=m; i++,p++) //对 HT (n+1~m)赋值语句
{
(*p).value=0,(*p).p=0,(*p).l=0,(*p).r=0;
}
int s1=0,s2=0;
for(i=n+1; i<=m; i++)
{
select(HT,i-1,&s1,&s2);
HT[s1].p=i,HT[s2].p=i;
HT[i].l=s1,HT[i].r=s2;
HT[i].value=HT[s1].value+HT[s2].value;
}
HC = (HuffCode)malloc((n+1)*sizeof(char *)); // 为字符指针数组分配内存
char *temp=(char *)malloc(n*sizeof(char));
temp[n-1]='\0';
for(i=1; i<=n; i++)
{
int start=n-2;
for(j=i,f=HT[i].p; f!=0; j=f,f=HT[f].p)
{
if(HT[f].l==j)
temp[start--]='0';
else
temp[start--]='1';
}
HC[i]=(char *)malloc((n-start)*sizeof(char));
strcpy(HC[i],&temp[++start]);
}
free(temp);
printf("\n 各个字符对应的编码\n");
for(i=1; i<=n; i++)
{
if(ww[i-1].ch==' ')
printf("空格 --> ");
else
printf("%c --> ",ww[i-1].ch);
puts(HC[i]);
}
*HC1 = HC;
*HT1 = HT;
}
void BecomeCode(HuffCode *HC1,int n,CHAR *Code1,char *Text,int *match)
{
HuffCode HC;
HC = *HC1;
CHAR Code;
Code = *Code1;
int len,i; //纯粹是用已知的文本 Text 和 HC 将文本转化为编码
len=strlen(Text);
Code = (char *)malloc((len*n+1)*sizeof(char));
Code[0]='\0';
for(i=0; i<len; i++)
{
if(Text[i]==' ')
strcat(Code,HC[1]);
else if(Text[i]<='Z'&&Text[i]>='A')
strcat(Code,HC[ match[Text[i]-'A'+1]+1 ]);
else
strcat(Code,HC[ match[Text[i]-'a'+27]+1 ]);
}
printf("\n文章编码为\n");
puts(Code);
*HC1 = HC;
*Code1 = Code;
FILE *fp;
if((fp=fopen("e:\\1.bin","w"))==NULL)
{
printf("打开文件失败,写操作失败");
}
fputs(Code,fp);
fclose(fp);
printf("加密后的文件是1.bin\n");
printf("加密后文件的路径是e:\\1.bin\n");
}
void Code_ToBe_Artical(CHAR Code,HuffTree HT,fact *Fact,int n) //纯粹的使用已经建好的树,由上至下进行查询
{
FILE *p;
char chh;
if((p=fopen("e:\\decode.txt","a+"))==NULL){
printf("ERROR");
}
printf("\n解码后的文件地址为e:\\decode.txt");
printf("\n将编码解码\n");
for(int i=0; Code[i]!='\0'; i++)
{
int m = n*2-1,ok=1;
while(1)
{
if(Code[i]=='0')
{
m=HT[m].l;
if(HT[m].l==0)
{
printf("%c",Fact[m-1].ch);
chh=Fact[m-1].ch;
fputc(chh,p);
break;
}
}
else if(Code[i]=='1')
{
m=HT[m].r;
if(HT[m].r==0)
{
printf("%c",Fact[m-1].ch);
chh=Fact[m-1].ch;
fputc(chh,p);
ok=0;
}
}
if(!ok)
break;
i++;
}
}
printf("\n");
fclose(p);
return ;
}
main.c
#include <HFTree.h>
int main()
{
FILE *fp;
char ch;
int a=0;
HuffTree HT=NULL;
HuffCode HC;
printf("请输入需要编码的文件地址:\n");
char Text[20000];
char location[100];
gets(location);
CHAR Code = NULL; //指针用于存储文章最终的编码
if((fp = fopen(location,"r")) == NULL)
{
printf("File open error!\n");
exit(0);
}
for( ; (ch = fgetc(fp)) != EOF;)
{
Text[a]=ch;
a++;
putchar(ch);
}
fclose(fp);
//gets(Text);
int len=strlen(Text);
int codeweight[54],match[54];
memset(codeweight,0,sizeof(codeweight));
for(int i=0; i<len; i++) // 统计频率 空格下标为 0 ,(A~Z)下标分别为(1~26) (a~z)下标分别为(27~52)
{
if(Text[i]==' ')
codeweight[0]++;
else if(isupper(Text[i]))
codeweight[Text[i]-'A'+1]++;
else
codeweight[Text[i]-'a'+27]++;
}
int n=0;
fact Fact[54]; // 由于不是每一个字符都出现在文章中,将 codeweight 数组录入Fact 结构体数组中
for(int i=0; i<=52; i++)
{
if(codeweight[i]!=0)
{
if(i==0)
Fact[n].ch=' ';
else if(i<=26)
Fact[n].ch=i+'A'-1;
else
Fact[n].ch=i+'a'-27;
match[i]=n;
Fact[n++].weight=codeweight[i];
}
}
HUFFTREE(&HT,Fact,n,&HC); //建树函数,附带完成每一个字符对应的编码
BecomeCode(&HC,n,&Code,Text,match);
Code_ToBe_Artical(Code,HT,Fact,n); //由全文的编码,用已经建立的哈弗曼树完成解码
return 0;
}
测试:
求个赞。。。