一、实验原理
1、本实验中Huffman编码算法
(1)将文件以ASCII字符流的形式读入,统计每个符号的发生频率;
(2)将所有文件中出现过的字符按照频率从小到大的顺序排列;
(3)每一次选出最小的两个值,作为二叉树的两个叶子节点,将和作为它们的根节点, 这两个叶子节点不再参与比较,新的根节点参与比较;
(4)重复3,直到最后得到和为1的根节点;
(5)将形成的二叉树的左节点标0,右节点标1,把从最上面的根节点到最下面的叶子节 点途中遇到的0、1序列串起来,得到了各个字符的编码表示。
2、Huffman编码的数据结构设计,在程序实现中使用一种叫做二叉树的数据结构实现Huffman编码。
(1)哈夫曼节点结构
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;
(2)哈夫曼码结构
typedef struct huffman_code_tag
{
unsigned long numbits;//该码所用的比特数
unsigned char *bits; //指向该码比特串的指针
} huffman_code;
二、实验步骤
1.huffman编码流程
(1)读入源文件
char memory = 0; //memory为1表示对内存编码
char compress = 1;//compress为1表示压缩,为0是解压
int opt;
//add by zhn
const char *file_in = NULL, *file_out = NULL;
const char *file_table=NULL;
FILE *in = stdin;//标准输入
FILE *out = stdout;//标准输出
//add by zhn
FILE *table;//输出码表
while((opt = getopt(argc, argv, "i:o:t:cdhvm")) != -1)//对argc,argv的解析,单个字符后跟一个冒号表示后面必须接参数
{
switch(opt)
{
case 'i':
file_in = optarg;
break;
case 'o':
file_out = optarg;
break;
//add by zhn
case 't':
file_table = 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;
default:
usage(stderr);
return 1;
}
}
(2)第一次扫描,统计文件中各个字符出现频率
#define MAX_SYMBOLS 256//共有256个字符
typedef huffman_node* SymbolFrequencies[MAX_SYMBOLS];
typedef huffman_code* SymbolEncoder[MAX_SYMBOLS];
static void
init_frequencies(SymbolFrequencies *pSF)
{
memset(*pSF, 0, sizeof(SymbolFrequencies));
}
static huffman_node*
new_leaf_node(unsigned char symbol)//新建叶子结点父节点为0