一、实验原理
霍夫曼编码
- Huffman Coding (霍夫曼编码)是一种无失真编码的编码方式,Huffman编码是可变字长编码(VLC)的一种。
- Huffman 编码基于信源的概率统计模型,它的基本思路是,出现概率大的信源符号编长码,出现概率小的信源符号编短码,从而使平均码长最小。
- 在程序实现中常使用一种叫做树的数据结构实现Huffman编码,由它编出的码是即时码
霍夫曼编码方法
- 统计符号的发生概率
- 把频率按从小到大的顺序排列
- 每一次选出最小的两个值,作为二叉树的两个叶子节点,将和作为它们的根节点,这两个叶子节点不再参与比较,新的根节点参与比较
- 重复3,直到最后得到和为1的根节点
- 将形成的二叉树的左节点标0,右节点标1,把从最上面的根节点到最下面的叶子节点途中遇到的0,1序列串起来,就得到了各个符号的编码。
二、实验流程及代码分析
实验流程
- 读入待编码的源文件
- 第一次扫描:统计文件中各个字符出现频率
- 建立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;
//子节点指针,分别代表0,1子节点指针
};
unsigned char symbol; //节点代表的符号
};
} huffman_node;
霍夫曼码结构:
typedef struct huffman_code_tag
{
unsigned long numbits; //该码所用的比特数
unsigned char *bits; //指向该码比特串的指针
// 76543210 | bit[0]
// ******98 | bit[1]
} huffman_code;
霍夫曼统计结构
typedef struct huffman_statistics_result
{
// 霍夫曼码字统计结果结构体,包含每个树叶节点的频率、位数、码字
float freq[256];
unsigned long numbits[256];
// 以字节为单位存放码字
unsigned char bits[256][100];
}huffman_stat;
代码分析
编码流程
主程序
读入待编码源文件及调用霍夫曼编码函数。
int main(int argc, char** argv)
{
char memory = 0;
// 是否读取内存数据操作,0为否
char compress = 1;
// 压缩或解压缩,1为压缩
// ......省略文件初始化
//step1:add by yzhang for huffman statistics
FILE * outTable = NULL;
// 输出的码字统计结果excel表格文件
//end by yzhang
/* Get the command line arguments. */
while((opt = getopt(argc, argv, "i:o:cdhvmt:")) != -1)
{
// 使用getopt函数读取命令行参数
switch(opt)
{
case 'i':
file_in = optarg;
break;
case 'o':
file_out = optarg;
break;
......
}
}
/* If an input file is given then open it. */
......
/* If an output file is given then create it. */
......
/* If an out table file is given then create it. */
......
if(memory)
{
// 是否读取内存数据进行压缩或解压操作
return compress ?
memory_encode_file(in, out) : memory_decode_file(in, out);
}
if(compress) //change by yzhang
// 是否对读入文件进行压缩或解压缩操作
huffman_encode_file(in, out,outTable);
//step1:changed by yzhang from huffman_encode_file(in, out) to huffman_encode_file(in, out,outTable)
else
huffman_decode_file(in, out);
//......省略关闭文件操作
return 0;
}
函数嵌套结构与功能
– huffman_encode_file 读文件,输出码表、编码后文件
– get_symbol_frequencies 构造叶子节点
– init_frequencies 初始化叶子节点
– new_leaf_node 建构造叶子节点
– huffST_getSymFrequencies 保存码字频率统计结果
– calculate_huffman_codes 组织霍夫曼二叉树结构
– qsort / SFComp 排序/规则为从小到大
– new_nonleaf_node 构造非叶子节点
– build_symbol_encoder 生成码字
– huffST_getcodeword 保存码字
– output_huffman_statistics 输出码字统计结果
– fprintf 写文件
– write_code_table 码表写入文件
– do_file_encode 读文件,查表编码,写入文件
霍夫曼编码函数
主函数
int
huffman_encode_file(FILE *in, FILE *out, FILE *out_Table)
//step1:changed by yzhang for huffman statistics from (FILE *in, FILE *out) to (FILE *in, FILE *out, FILE *out_Table)
{
SymbolFrequencies sf;
// 叶子结点结构体数组
SymbolEncoder *se;
// 码节点结构体数组
huffman_node *root = NULL;
// 根节点,初始化为空
int rc;
unsigned int symbol_count;
// 总符号数
//step2:add by yzhang for huffman statistics
huffman_stat hs;
// 霍夫曼统计结构体
//end by yzhang
/* Get the frequency of each symbol in the input file. */
symbol_count = get_symbol_frequencies(&sf, in);
// 为出现过的符号建立叶子节点,并返回总符号数
//step3:add by yzhang for huffman statistics,... get the frequency of each symbol
huffST_getSymFrequencies(&sf,&hs,symbol_count);
// 保存各码字频率统计结果,给霍夫曼统计结构体
//end by yzhang
/* Build an optimal table from the symbolCount. */
se = calculate_huffman_codes(&sf);
// 构造非叶子节点、组织霍夫曼二叉树结构、生成霍夫曼码字,返回霍夫曼码字结构体
root = sf[0];
// 保存根节点
//step3:add by yzhang for huffman statistics... output the statistics to file
huffST_getcodeword(se, &hs);
// 保存码字
output_huffman_statistics(&hs,out_Table);
// 输出统计结果
//end by yzhang
/* Scan the file again and, using the table
previously built, encode it into the output file. */
rewind(in);
// 文件指针重新指向首地址
rc = write_code_table(out, se, symbol_count);
// 将码表写入文件
if