一.Huffman编码的原理
Huffman Coding (霍夫曼编码)是一种无失真编码的编码方式,Huffman 编码是可变字长编码(VLC)的一种。
Huffman 编码基于信源的概率统计模型,它的基本思路是出现概率大的信源符号编长码,出现概率小的信源符号编短码,从而使平均码长最小。
在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
在程序实现中常使用一种叫做树的数据结构实现 Huffman 编码,由它编出的码是即时码。
huffman编码流程 |
---|
统计符号的发生概率 |
将频率从小到大排序 |
每一次选出最小的两个值,作为二叉树的两个叶子节点,将和作为它们的根节点,这两个叶子节点不再参与比较,新的根节点参与比较 |
重复第三步 |
将形成的二叉树的左节点标 0,右节点标 1,把从最上面的根节点到最下面的叶子节点途中遇到的 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;
- huffman编码
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]. */
/* 码字, 码字的第 1 位存于 bits[0]的第 1 位,
码字的第 2 位存于 bits[0]的第的第 2 位,
码字的第 8 位存于 bits[0]的第的第 8 位,
码字的第 9 位存于 bits[1]的第的第 1 位 */
unsigned char *bits;
} huffman_code;
- huffman表格
/*对输出的表格进行结构化 针对huff_run main函数 */
//输出的表格有些变量可以直接写进结构体里,如此表格可以很快输出,再加入新变量也不会很复杂
typedef struct huffman_statistics_result
{
float freq[256];
unsigned long numbits[256];
unsigned char bits[256][100];
}huffman_stat;
二.实验流程
三.代码分析
1.读取待编码的文件
首先对命令行的参数进行定义
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",//输出huffman统计表格
out);
}
主函数,对命令行参数进行设计
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;
/* Get the command line arguments. */
while((opt = getopt(argc, argv, "i:o:cdhvmt:")) != -1) //读取命令行参数的选项
{
switch(opt)
{
case 'i':
file_in = optarg;//选择i,输入文件
break;
case 'o':
file_out = optarg;//选择o,输出文件
break;
case 'c':
compress = 1;//选择c,压缩
break;
case 'd':
compress = 0;//选择d,解压缩
break;