一.实验原理
哈夫曼编码是可变字长编码(VLC)的一种,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,概率大的符号码长短,概率小的符号码长长,是一种无失真编码。
哈夫曼编码流程示意:
(1)统计所有符号发生的概率
(2)按照概率从小到大对符号进行排序
(3)所有符号为树叶节点,将概率最小的两个树叶节点合并为中间结点,并重新进行排序,重复直到生成根节点,根节点概率为1
(4)由此可形成huffman树,每个中间结点的左子结点为0,右子结点为1,生成huffman编码
在编程实现huffman编码时,利用二叉树结构的特点进行编写。
节点结构:
typedef struct huffman_node_tag
{
unsigned char isLeaf;//是否为树叶
unsigned long count;//信源中出现的频数
struct huffman_node_tag *parent;//父节点指针
union//如果不是树叶,则为该节点左右孩子的指针;否则为某个信源符号
{
struct
{
struct huffman_node_tag *zero, *one;
};
unsigned char symbol;
};
} huffman_node;
码字结构:
typedef struct huffman_code_tag
{
/* 码字长度(以bit为单位) */
unsigned long numbits;
/* 码字,码字第1位存于bits[0]第1位,
码字第2位存于bits[0]第2位,
码字第8位存于bits[0]第8位,
码字第9位存于bits[1]第1位。*/
unsigned char *bits;
} huffman_code;
二.实验流程
编码流程:
读入待编码源文件 |
第一次扫描:统计文件中各个字符出现的频率 |
建立huffman树 |
将码表及其他必要信息写入输出文件 |
第二次扫描:对原文件编码并输出 |
解码流程:
读入编码文件 |
提取必要信息,依照码表重建Huffman树 |
从根节点开始依据从文件中读取的Huffman码字沿树行走,至叶节点时输出符号至输出文件,并回到根节点 |
所有码字解码完毕,文件解码完成 |
三.关键代码分析
运行主函数,读入文件:
static void//定义命令行参数
usage(FILE* out)
{
fputs("Usage: huffcode [-i<input file>] [-o<output file>] [-d|-c]\n"
"-i - input file (default is standard input)\n"//输入文件
"-o - output file (default is standard output)\n"//输出文件
"-d - decompress\n"//解压缩
"-c - compress (default)\n"//进行压缩
"-m - read file into memory, compress, then write to file (not default)\n",//读内存
"-t - output huffman statistics\n",//输出统计表格
out);
}
int
main(int argc, char** argv)
{
char memory = 0;//为0时进行文件的编解码,为1时进行内存的编解码
char compress = 1;//为0时解码,为1时编码
int opt;//选项,getopt()的返回值,getopt()处理'-'开头的命令行参数
const char *file_in = NULL, *file_out = NULL;
//step1:add by yzhang for huffman statistics
const char *file_out_table = NULL;
//end by yzhang
FILE *in = stdin;
FILE *out = stdout;
//step1:add by yzhang for huffman statistics
FILE * outTable = NULL;
//end by yzhang
/*获取命令行参数选项*/
while