哈夫曼编/译码器
利用哈夫曼编码进行信息通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
二. 设计内容
哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。 在计算机信息处理中。
哈夫曼编码”是一种一致性编码法(又称“熵编码法”),用于数据的无损耗压缩。这一术语是指使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数据的目的)。这种方法是由David.A.Huffman发展起来的。 例如,在英文中,e的出现概率很高,而z的出现概率则最低。当利用哈夫曼编码对一篇英文进行压缩时,e极有可能用一个位(bit)来表示,而z则可能花去25个位(不是26)。用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个位。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。若能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。
利用哈夫曼编码进行信息通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。
——参考来自百度百科介绍
三.概要设计
1.建立哈夫曼树:读入文件(.souce),统计文件中字符出现的频度,并以这些字符的频度作为权值,建立哈夫曼树。
2.编码:利用已建立好的哈夫曼树,获得各个字符的哈夫曼编码,并对正文进行编码,然后输出编码结果,并存入文件(.code)中。
3.译码:利用已建立好的哈夫曼树将文件(.code)中的代码进行译码,并输出译码结果,并存入文件(.decode)中。
4.利用位操作,实现文件的压缩与解压。
1.void tongji(); //统计字符出现频率,对其进行冒泡排序
2.void mima(); //密码登录界面
3.void Huffman(); //利用字符与出现频率作为权重,构造huffman树
4.void menu(); //菜单页面
5.void HuffmanCode();//为每个字符编码
6.void Encode(); //利用huffman树字符对应前缀为文件编码
7.void Decode(); //利用huffman树字符对应前缀为文件译码
8.void yasuo(); //利用位操作对huffman文件进行压缩将8位二进制化为一位十进制数
9.char bianyasuo(char str[]);//位操作将8位01数组化为一位十进制
10.void jieya(); //解压为二进制文件
设计字符的结构体(Count)其中包含字符(c)和出现次数(count)并且定义数组Leaf[maxsize]
struct Count
{
int c;
int count;
};
设计哈夫曼树的结构体(hufmtree),其中包含权重、左右孩子、父母和要编码的字符。用这个结构体(hufmtree)定义个哈夫曼数组(hufmtree tree[maxsize];)。
typedef struct
{
char ch;
int weight;
int lchild,rchild,parent;
}hufmtree;
hufmtree tree[1000];
设计编码类型的结构体(Codetype)其中包含字节(bits)和开始位置(start)还有与编码对应的字符(char ch)
typedef struct
{
char bits[3000];
int start;
char ch; //与编码对应的字符
}Codetype; Codetype code[maxsize];
#include<stdio.h>
#include<stdlib.h>
#include<io.h>
#include<conio.h>
/* #define n 5 //叶子数目
#define m (2*n-1) //结点总数*/
#define maxval 1000
#define maxsize 100 //哈夫曼编码的最大位数
struct Count
{
int c;
int count;
};
typedef struct
{
char ch;
int weight;
int lchild,rchild,parent;
}hufmtree;
hufmtree tree[400];
typedef struct
{
char bits[400];
int start;
char ch; //与编码对应的字符
}Codetype;
Codetype code[200];
struct Count leaf[128];
int zifunum=0;
void tongji();
void Huffman();
void menu();
void HuffmanCode();//为每个字符编码
void Encode();
void Decode();
void jieya();
void main()
{
int num;
//system("cls");
while(1)
{
system("cls");
printf("************************************************************************\n");
printf("--------------------------huffman编译码器-------------------------------\n");
printf("--------------------------请选择 所需项目-------------------------------\n");
printf("--------------------------1.统计字符出现频率----------------------------\n");
printf("--------------------------2.建立huffman树-------------------------------\n");
printf("--------------------------3.huffman编码---------------------------------\n");
printf("--------------------------4.huffman译码---------------------------------\n");
printf("--------------------------5.文件压缩------------------------------------\n");
printf("--------------------------6.文件解压------------------------------------\n");
printf("************************************************************************\n"); printf("\n");
scanf("%d",&num);
switch(num)
{
case 1: tongji();break;
case 2: Huffman();break;
case 3: HuffmanCode();break;
case 4: Decode(); ;break;
case 5: tongji();Huffman();HuffmanCode();break;
case 6: break;
}
}
// menu();
}
void tongji()
{ int mi,ni,tmp2,tmp3;
struct Count counter[128];
int i=0;
for(i=0;i<128;i++) {counter[i].c=i;counter[i].count=0;}
FILE* fp;
fp=fopen("d:\\b.souce","r");
char flag=0xA;
int lines=0;
char tline[128]={'\0'};
char* cp=tline;
char tmp;
while(!feof(fp))
{
tmp=getc(fp);
counter[tmp].count++;//强制转换,ASCII码0-256在相应位置++
}
//sort
for(mi=0;mi<128;mi++)//给count数组加字符
{
for(ni=mi+1;ni<128;ni++)
{
if(counter[mi].count>=counter[ni].count)
{
tmp2=counter[mi].count;
counter[mi].count=counter[ni].count;
counter[ni].count=tmp2;
tmp3=counter[mi].c;
counter[mi].c=counter[ni].c;
counter[ni].c=tmp3;
}
}
}
//sort end
for(i=0;i<128;i++)
{
if(counter[i].count>0)
leaf[i]=counter[i];
}
int _chars=0;
for(i=0;i<128;i++)
{
if(counter[i].count>0)
{
zifunum++;
_chars+=counter[i].count;
if(counter[i].c==0xA) //\n
{
printf("\\n Number of occurrences=%d\n",counter[i].count);
}
else
if(counter[i].c==0x20) //space
{
printf("space Number of occurrences=%d\n",counter[i].count);
}
else
if(counter[i].c==0x9) //\t
{
printf("\\t Number of occurrences=%d\n",counter[i].count);
}
else
if(counter[i].c==0x0) //NULL
{
printf("NULL Number of occurrences=%d\n",counter[i].count);
}
else
if(counter[i].c==0xD) //
{
printf("OxD Number of occurrences=%d\n",counter[i].count);
}
else /*Visible characters*/printf("%c Number of occurrences=%d\n",counter[i].c,counter[i].count);
}
}
printf("zifunum %d\n",zifunum);
printf("File the total number of characters=%d\n",_chars);
fclose(fp);
getch();
}
void Huffman() //构造哈夫曼树
{
int i,e,j,p1,p2;e=0;
int small1,small2,n;
n=zifunum;
for(i=0;i<128;i++)
{
if(leaf[i].count!=0) //赋权值
{ tree[e].weight=leaf[i].count;
tree[e].ch=leaf[i].c;
e++;
}
}
for(i=0;i<2*n-1;i++) //初始化数组
{
tree[i].parent=-1;
tree[i].lchild=-1;
tree[i].rchild=-1;
}
for(i=n;i<2*n-1;i++) //进行n-1次合并产生n-1个新结点
{
p1=p2=0;
small1=small2=maxval;//使最小值和次小值都为maxval
for(j=0;j<=i-1;j++) //选出两个权值最小的根结点
if(tree[j].parent==-1)
if(tree[j].weight<small1) //查找最小权并用p1记录其下标
{
small2=small1;
small1=tree[j].weight;
p2=p1;
p1=j;
}
else
if(tree[j].weight<small2)//查找次小权并用p2记录其下标
{
small2=tree[j].weight;
p2=j;
}
tree[p1].parent=tree[p2].parent=i;
tree[i].weight=tree[p1].weight+tree[p2].weight;
tree[i].lchild=p1;
tree[i].rchild=p2;
}
}
void HuffmanCode()//为每个字符编码
{
int i,j,p,k,n;
n=zifunum;
for(i=0;i<n;i++)
{
printf("%c ",tree[i].ch);
code[i].start=n-1;
j=i;
p=tree[i].parent;
while(p!=-1)
{
if(tree[p].lchild==j)
code[i].bits[code[i].start]='0';
else
code[i].bits[code[i].start]='1';
code[i].start--;
j=p;
p=tree[p].parent;
}
for(k=code[i].start+1;k<n;k++)
printf("%c",code[i].bits[k]);
printf("\n");
}
Encode();
getch();
}
void Encode()//将文章存储在磁盘文件中并显示编码
{
FILE *in;FILE *out;
char ch, infile[10],outfile[10];
int i,k,n;
n=zifunum;
printf("输入源文件名:\n");
scanf("%s",infile);
printf("输入保存文件名:\n");//保存编码后的文件名
scanf("%s",outfile);
if((in=fopen(infile,"r"))==NULL)//文件打不开
{
printf("cannot open infile\n");
exit(0);//终止程序
}
if((out=fopen(outfile,"w"))==NULL)
{
printf("cannot open outfile\n");
exit(0);//终止程序
}
printf("\n编码后的文件是:\n");//英文文章将以编码的形式显示并存储在磁盘文件中
while(!feof(in))//读取数据的文件没有结束
{
ch=fgetc(in);//从in指针指向的文件中读入字符
for(i=0;i<n;i++)
{
if(tree[i].ch==ch)
for(k=code[i].start+1;k<n;k++)//把第i种字符的哈夫曼编码
{
printf("%c",code[i].bits[k]);
fputc(code[i].bits[k],out);
}
}
}
printf("\n");
fclose(in); //关闭in指向的文件
fclose(out); //关闭out指向的文件
}
void Decode() //译码
{
int n;
n=zifunum;
int p=2*n-2; //根结点
char ch,ps;
FILE *jie;//
FILE *out;//打开保存了译码的文件
char jieyafile[20]={"D://d.decode"};
char outfile[20]={"D://a.code"};
printf("输入译码文件:\n");
//scanf("%s",outfile);
printf("输入想保存的解压文件名:\n");
//scanf("%s",jieyafile);
if((jie=fopen(jieyafile,"w"))==NULL)
{
printf("cannot open jieyafile\n");
exit(0); //终止程序
}
if((out=fopen(outfile,"r"))==NULL)
{
printf("cannot open outfile\n");
exit(0); //终止程序
}
printf("\n原文件是:\n");//该部分是将编码还原成字符
while(ch!='2')
{
while( tree[p].lchild!=-1)
{ ch=fgetc(out);
//从out指向的文件中读取一个字符
if(ch=='0')//左孩子
{ p=tree[p].lchild;}//左孩子的编码赋给p
if(ch=='1')//右孩子
{ p=tree[p].rchild;}//右孩子的编码赋给p
else if(ch=='2')
break;
}
if(tree[p].lchild==-1) //走到叶子结点
{
printf("%c",tree[p].ch);//输出该叶子结点的字符
ps=int(tree[p].ch);
fputc(ps,jie);
}
p=2*n-2; //回到根结点
}
getch();
fclose(jie);//关闭jie指向的文件
fclose(out);//关闭out指向的文件
}
/*
void yasuo()
{
FILE *fp;
int fno,fsize;
int n[4];
char ya[400];
fp=fopen("filename","w");
fno=fileno(fp);
fsize=filelength(fno);
while(!feof(fp))
{
fread(*n,4,fsize/4,fp);
}
}
*/
void yasuo()
{
int n;
int i;
char str[4]
FILE *fp;
FILE *fp2;
if((fp=fopen("D://a.code","r"))==NULL)
{
printf("cannot open a.code\n");
exit(0); //终止程序
}
if((fp=fopen("D://a.code","w"))==NULL)
{
printf("cannot open a.code\n");
exit(0); //终止程序
}
while(!feof(fp))
fread(&stu[i],1,fp);
}
void jieya() //译码
{
int n;
n=zifunum;
int p=2*n-2; //根结点
char ch,ps;
FILE *jie;//
FILE *out;//打开保存了译码的文件
char jieyafile[20]={"D://d.decode"};
char outfile[20]={"D://a.code"};
printf("输入译码文件:\n");
//scanf("%s",outfile);
printf("输入想保存的解压文件名:\n");
//scanf("%s",jieyafile);
if((jie=fopen(jieyafile,"w"))==NULL)
{
printf("cannot open jieyafile\n");
exit(0); //终止程序
}
if((out=fopen(outfile,"r"))==NULL)
{
printf("cannot open outfile\n");
exit(0); //终止程序
}
printf("\n原文件是:\n");//该部分是将编码还原成字符
while(ch!='2')
{
while( tree[p].lchild!=-1)
{ ch=fgetc(out);
//从out指向的文件中读取一个字符
if(ch=='0')//左孩子
{ p=tree[p].lchild;}//左孩子的编码赋给p
if(ch=='1')//右孩子
{ p=tree[p].rchild;}//右孩子的编码赋给p
else if(ch=='2')
break;
}
if(tree[p].lchild==-1) //走到叶子结点
{
printf("%c",tree[p].ch);//输出该叶子结点的字符
ps=int(tree[p].ch);
fputc(ps,jie);
}
p=2*n-2; //回到根结点
}
getch();
fclose(jie);//关闭jie指向的文件
fclose(out);//关闭out指向的文件
}
void decode() //依次读入电文,根据哈夫曼树译码
{
int i,j=0,s,g=0,n;
FILE *fp1,*fp2;
char b[1000];
n=zifunum;//从根结点开始往下搜索
i=2*n-2;
if((fp1=fopen("D://a.code","r"))==NULL)
{
printf("读文件出错!!按任意键退出。");
getch(); exit(0);
}
if((fp2=fopen("D://cc.decode","wt"))==NULL)
{
printf("文件出错!!按任意键退出。");
getch(); exit(0);
}
while ((s=fgetc(fp1))!=EOF)
{
b[g++]=s;
}
printf("译码后的字符为:\n");
while(b[j]!='2')
{
if(b[j]=='0')
i=tree[i].lchild; //走向左孩子
else
i=tree[i].rchild; //走向右孩子
if(tree[i].lchild==-1) //tree[i]是叶结点
{
fprintf(fp2, "%c",tree[i].ch);
printf("%c",tree[i].ch);
i=2*n-2; //回到根结点
}
j++;
}
printf("保存成功!!请按任意键继续。\n");
fclose(fp2);
fclose(fp1);
printf("按任意键返回主菜单!!!");
getch();
// menu();
}

本文介绍了哈夫曼编码的概念,它是一种用于数据无损压缩的一致性编码法,通过构建哈夫曼树实现。文章详细阐述了如何建立哈夫曼树,对文本进行编码和解码的过程,以及利用位操作进行文件压缩和解压的方法。
2066

被折叠的 条评论
为什么被折叠?



