文本文件的二进制预统计 Huffman 编解码
一、实验目的
(1) 熟悉 Huffman 编解码算法;
(2) 理解 Huffman 编码的最佳性。
二、实验内容
1、编程思想
霍夫曼(Huffman)编码是 1952 年为文本文件而建立,是一种统计编码。属于无损
压缩编码。霍夫曼编码的码长是变化的,对于出现频率高的信息,编码的长度较短;而
对于出现频率低的信息,编码长度较长。这样,处理全部信息的总码长一定小于实际信
息的符号长度。
计算机编程实现时,首先统计带编码的文本文件中各个字符出现的概率,然后将概
率作为节点的权值构建 huffman 树。编码时从叶子节点出发,如果这个节点在左子树上,
则编码 0,否则编码 1,直到根节点为止,所得到的 01 序列即为该叶子节点的编码。所
有叶子节点的编码构成一个码本。
有两种译码方法:(1)按位读入码字,从已建好的 Huffman 树的根节点开始,若码
字为“0”,则跳到左子树,若为“1”则跳到右子树,直到叶子结点为止,输出叶子接
点所表示的符号。(2)由于 Huffman 编码是唯一码,还有另一种译码方法,每读入一位
编码就去码本中去匹配相应的码字,若匹配不成功,则继续读入下一个编码,直到匹配
成功为止。显然前一种方法比较简便,本程序采用便是该方法。
2、程序流程图
开始编码
读入文本文件
统计各符号概率
构建 Huffman 树
保存码本
开始译码
读入码本
定位到根节点
读入 1 位码字
N
码字为 1?
Y
编码
跳到左子树
跳到右子树
输出编码文件
编码结束
N
N
叶子结点?
?
Y
输出字符
码字读完?
Y
译码结束
3、编程实现
本实验采用用 C 语言程序语言,VC++ 6.0 编程环境。
(1)数据结构
构造存放符号及其权值的存储结构如下
typedef struct
{
unsigned char symbol;
int sweight;
//符号的 ASCII 码值
//权值
}syml_weit;
syml_weit *sw;
sw
symbol
sweight
构造 huffman 树存储结构如下:
typedef struct
{
//权值
//左孩子地址
int weit;
int lchd;
int rchd; //右孩子地址
int part; //双亲地址
}hufmtree;
hufmtree *htree;
htree
weit
rchd
part;
lchd
(2)函数
本程序共包含 5 个函数:
一个主函数:
void main(),
4 个子函数:
void CountWeight(unsigned char *DataBuf,int FileLen);
void BuildTree();
void HufmCode(unsigned char *DataBuf,int FileLen);
void HufmDCode(unsigned char *CDataBuf,int CDataLen);
其功能分别是:
CountWeight----计算文本文件中各符号的权值;
BuildTree-------构建 Huffman 树,形成码本;
HufmCode---------对文本文件进行编码;
HufmDCode-------进行译码。
(3)存储器
本程序共包含 4 个存储器:
unsigned char *DataBuf;
unsigned char *CodeBook; //存放码本
unsigned char *CodeBuf;
//存放编好的码
//存放文本文件数据的内存空间
unsigned char *DCodeBuf; //存放译码后数据
详细代码见附件。
三、实验结果
程序运行后会形成 3 个文件:
codebook.txt-------码本文件;
abc_code.txt-------编码文件;
abc_dcode.txt------译码文件。
经比较译码后的文件与待编码文件相同,编译码成功实现
为了编写简单,编码文件输出时未采用的比特输出的方式,而采用了字
节输出方式,即每个字节代表一个码字。这样输出文件大小是理论值的 8 倍。
因此计算压缩率时应该除以 8 才是真正的压缩率。
压缩率 =
已编码文件大小/8
待编码文件大小
*100%
= 42231/8/9433*100%
=
55.96178%
附件:完整程序代码
#include
#include
#define MaxNo 256
#define NULL 0
typedef struct
{
unsigned char symbol;
int sweight;
}syml_weit;
syml_weit *sw;
typedef struct
{
int weit;
int lchd;
int rchd;
int part;
}hufmtree;
hufmtree *htree;
//存放符号及其权值
//存放Huffman树
int leafnode,totalnode;
//叶子节点个数 和 整棵树的所有节点
//存放文本文件数据的内存空间
unsigned char *DataBuf;
unsigned char *CodeBook; //存放码本
unsigned char *CodeBuf;
unsigned char *DCodeBuf;
int CodeBookLen;
int CodeLen;
int DCodeLen;
//码本长度
//码长度
//存放编好的码
void CountWeight(unsigned char *DataBuf,int FileLen);
void BuildTree();
void HufmCode(unsigned char *DataBuf,int FileLen);
void HufmDCode(unsigned char *CDataBuf,int CDataLen);
void main()
{
FILE *fp1,*fp2,*fp3,*fp4;
char filepath[]="abc.txt";
int FileLen;
DataBuf = new unsigned char[1024*1024];
//文件读取指针
//读入的文件长度
printf("=====Huffman Code and Decode by LiYingle@NDSC==== \n\n");
if ((fp1=fopen(filepath,"rb"))==NULL) //读入文本文件
{
printf("abc open error!");
}
FileLen = fread(DataBuf,1,1024*1024,fp1);
CountWeight(DataBuf,FileLen);
BuildTree();
HufmCode(DataBuf,FileLen);
HufmDCode(CodeBuf,CodeLen);
//计算权值
//建树
//编码
//译码
///// 输出码本文件和压缩率
if ((fp2=fopen("codebook.txt","w"))==NULL)
{
printf("abc_codebook open error!");
}
fprintf(fp2,"ASCII WEIGHT
for (int i=0;i
fwrite(DCodeBuf,1,DCodeLen,fp4);
fclose(fp1);
fclose(fp2);
fclose(fp3);
fclose(fp4);
delete[] DataBuf;
delete[] CodeBook;
delete[] CodeBuf;
delete[] DCodeBuf;
}
void CountWeight(unsigned char *DataBuf,int FileLen)
{
int i=0,sum=0;
int counter[MaxNo]={0x0};
for (i=0;i
htree[j].rchd = 0;
htree[j++].part = 0;
}
}
}
void BuildTree()
{
int i=0;
int j=leafnode;//非叶子节点的开始
int w1,w2;//两个最小的权值
int p1=-1,p2=-1;
for (int leaf=0;leaf=w1)
{
p2 = i;
w2 = htree[i].weit;
}
else
{
p2 = p1;
w2 = w1;
p1 = i;
w1 = htree[i].weit;
}
break;
}
}
}