Huffman编解码算法实现与压缩效率分析
一.背景知识及相关公式
1.信源熵
信源熵是信息的度量单位,一般用H表示,单位是比特,对于任意一个随机变量,它的熵定义为
,变量的不确定性越大,熵也就越大。
2.Huffman编码
(1)Huffman编码是一种无失真编码的编码方式,可变长编码的一种;
(2)Huffman编码基于信源的概率统计模型,它的基本思路是,出现概率小的信源符号编长码,出现概率大的信源符号编短码,从而使平均码长最小。
(3)在程序实现中常使用一种叫做树的数据结构实现Huffman编码,由它编出的码是即时码。
3.Huffman编码算法
(1)将文件以ASCII字符流的形式读入,统计每个符号的发生频率;(2)将所有文件中出现过的字符按照频率从小到大的顺序排列 ;
(3)每一次选出最小的两个值,作为二叉树的两个叶子节点,将和作为它们根节点,这两个叶子节点不再参与比较,新的根节点参与比较;
(4)重复 3,直到最后得和为1的根节点;
(5)将形成的二叉树左节点标0,右节点标1,把从最上面的根节点到最下面叶子点途中遇到的0、1序列串起来,得到了各个字符的编码表示。
二.实验过程
1.数据结构
(1)Huffman节点
typedef struct huffman_node_tag
{
unsigned char isLeaf; /*是否为叶节点,1表示是叶节点,0表示不是叶节点*/
unsigned long count; /*文件中符号出现的频数*/
struct huffman_node_tag *parent; /*父节点的指针*/
union/*如果是树叶,则此项为该节点的左右孩子的指针,否则为某个信源符号*/
{
struct
{
struct huffman_node_tag *zero, *one;/*该节点左右孩子的指针*/
};
unsigned char symbol;/*信源符号,一个字节的二进制数值*/
};
} huffman_node;
typedef struct huffman_node_tag
{
unsigned char isLeaf; /*是否为叶节点,1表示是叶节点,0表示不是叶节点*/
unsigned long count; /*文件中符号出现的频数*/
struct huffman_node_tag *parent; /*父节点的指针*/
union/*如果是树叶,则此项为该节点的左右孩子的指针,否则为某个信源符号*/
{
struct
{
struct huffman_node_tag *zero, *one;/*该节点左右孩子的指针*/
};
unsigned char symbol;/*信源符号,一个字节的二进制数值*/
};
} huffman_node;
(2)
Huffman码字节点
typedef struct huffman_code_tag
{
unsigned long numbits;/*码字长度 */
/*码字的第1位存于bits[0]的第1位,
码字的第2位存于bits[0]的第2位,
码字的第8位存于bits[0]的第8位,
码字的第9位存于bits[1]的第1位,*/
unsigned char *bits;
} huffman_code;
2.Huffman编码
(1)编码流程
(2)读入源文件
int main(int argc, char** argv)
{
char memory = 0;
char compress = 1;
int opt;
const char *file_in = NULL, *file_out = NULL;
const char *file_out_table = NULL;
FILE *in = stdin;
FILE *out = stdout;
FILE * outTable = NULL;
/*获取命令行参数*/
while((opt = getopt(argc, argv, "i:o:cdhvmt:")) != -1)
{
switch(opt)
{
case 'i'://输入文件
file_in = optarg;
break;
case 'o'://输出文件
file_out = optarg;
break;
case 'c'://编码
compress = 1;
break;
case 'd'://解码
compress = 0;
break;
case 'h'://参数用法输出到屏幕
usage(stdout);
return 0;
case 'v'://版本信息输出到屏幕
version(stdout);
return 0;
case 'm':
memory = 1;//对内存数据进行编码
break;
case 't'://编码结果输出
file_out_table = optarg;
break;
default:
usage(stderr);
return 1;
}
}
/* If an input file is given then open it. */
if(file_in)
{
in = fopen(file_in, "rb");
if(!in)
{
fprintf(stderr,
"Can't open input file '%s': %s\n",
file_in, strerror(errno));
return 1;
}
}
/* If an output file is given then create it. */
if(file_out)
{
out = fopen(file_out, "wb");
if(!out)
{
fprintf(stderr,
"Can't open output file '%s': %s\n",
file_out, strerror(errno));//strerror函数返回指向错误原因字符串的指针,errno是错误代码,errno.h中包含很多错误定义
return 1;
}
}
if(file_out_table)
{
outTable = fopen(file_out_table, "w");
if(!outTable)
{
fprintf(stderr,
"Can't open output file '%s': %s\n",
file_out_table, strerror(errno));
return 1;
}
}
if(memory)//memeory=1对内存数据进行编码,反之解码
{
return comp