一,实验原理
1,Huffman编码原理:
1)统计:将每个符号出现的概率进行统计,并且从小到大排序。
2)合并:将出现概率最小的两个符号概率进行合并,反映在二叉树上:将两个树叶节点合并得到一个父节点。重复此步骤直到根节点。
3)编码:二叉树按照左0右1的规则编码,遍历整棵树,然后自根节点向下到每个树叶节点,可以得到每个树叶节点的编码。
2,基本数据格式定义:
节点和码字的定义见代码。
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;//除树叶节点外,每个节点必有两个指向子节点的指针,左0右1
};
unsigned char symbol;
};
} huffman_node;
typedef struct huffman_code_tag//码表的定义
{
/* The length of this code in bits. */
unsigned long numbits;
/* The bits that make up this code. The first
bit is at position 0 in bits[0]. The second
bit is at position 1 in bits[0]. The eighth
bit is at position 7 in bits[0]. The ninth
bit is at position 0 in bits[1]. */
unsigned char *bits;
} huffman_code;
二,实验流程及代码分析
1,Huffman编码流程
1)读入文件
2)第一次扫描文件,统计文件中各个字符出现的概率
3)建立码树
4)将码表写入文件
5)第二次扫描文件,对源文件进行编码并输出
2,代码分析
1)主函数操作:读取文件
int
main(int argc, char** argv)
{
char memory = 0;//指示是否操作内存数据
char compress = 1;//此处表示编码过程,若为0则表示为解码过程
int opt;
const char *file_in = NULL, *file_out = NULL;
FILE *in = stdin;
FILE *out = stdout;
/* Get the command line arguments. */
while((opt = getopt(argc, argv, "i:o:cdhvm")) != -1)//读取命令行参数,此处利用getopt函数读取,最后一个参数是单个字符。
{
switch(opt)
{
case 'i'://input
file_in = optarg;
break;
case 'o'://output
file_out = optarg;
break;
case 'c'://code
compress = 1;
break;
case 'd'://decode
compress = 0;
break;
case 'h'://输出参数用法的说明
usage(stdout);
return 0;
case 'v'://输出版本号的信息
version(stdout);
return 0;
case 'm'://对内存解码
memory = 1;
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));
return 1;
}
}
if(memory)//对内存数据进行编解码操作
{
return compress ?
memory_encode_file(in, out) : memory_decode_file(in, out);
}
return compress ?
huffman_encode_file(in, out) : huffman_decode_file(in, out);
}
2)对文件编码:编码函数->huffman_encode_file()
int
huffman_encode_file(FILE *in, FILE *out)
{
SymbolFrequencies sf;
SymbolEncoder *se;
huffman_node *root = NULL;
int rc;
unsigned int symbol_count;
/* Get the frequency of each symbol in the input file. */
symbol_count = get_symbol_frequencies(&sf, in);//第一遍扫描,统计字节出现的频率(再文件中,符号用字节表示)
/* Build an optimal table from the symbolCount. */
se = calculate_huffman_codes(&sf);//建立码树,得到码表
root = sf[0];//表示根节点为sf[0]
/* 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(rc == 0)
rc = do_file_encode(in, out, se);//