一、目标与要求
一:程序的设计内容
从文件中读入一个英文文本文件(大于2048个字符)
采用统计字符出现的频率;
采用哈夫曼编码方法生成编码表
利用此编码表对文件进行压缩编码,保存到文件,同时存入编码表
读入编码文件和编码表,进行解码后保存到文件中,与原始文件进行对比
二:基本要求
完成文本文件的读写操作;
编写通用的树(使用模板类);
生成哈夫曼树;
三:分析程序及实验方法
二、工具/准备工作
VC6.0,节点类、串类、链表类、树结点类软件包
三、实验分析
首先构建哈夫曼树类
成员变量有:
HuffmanTreeNode<WeightType> *nodes;
CharType *str; //存放字符
String *LeafCharCodes;//存放叶子编码
int num; //总结点数,2n-1
int n; //叶子结点数,n
int curpos; //译码时路径点
成员函数有:
void Select(int cur, int &r1, int &r2); //选择没有父母的权最小的两个结点
HuffmanTree(CharType *ch,WeightType *w,int n);//构造函数
~HuffmanTree(); //析构函数
String Encode(CharType ch); //辅助编码
LinkList<CharType> Decode(String strCode); //译码
主程序中,先构建统计字符频率的函数,根据具体文本生成相应的哈夫曼树。
之后将文本编码、连同权重等信息保存至文件。
最后读取编码文件及权重文件,生成哈夫曼树,利用生成的哈夫曼树解码,将解出的文件与原文本比对。
四、实验步骤
1.引入相关函数包,编写huffman_tree类。
编码时,字符的编码就是从根结点到该叶子结点的路径码,左为0右为1,具体操作时可以从叶子结点出发回到根结点,如果结点是双亲的左孩子,则在编码链表最前端插入0,否则插入1。
解码时需要借助哈夫曼树,其过程是:依次读入编码,从哈夫曼树的根结点出发,若当前读入0,则走向左孩子,否则走向右孩子。当到达某一叶子结点时便译出相应的字符。然后重新从根结点出发继续译码,直至文件结束。
2.完成统计字符频率sort函数的编写
3.利用权值表生成哈夫曼树,压缩编码并保存到文件
4.读取文件,生成哈夫曼树进行解码,将解码后的文章与原文比对,调试完善程序
五、测试与结论
原文本:
编码后的文件:
字符及权值表:
解码后的文本:与原文本相同
六、实验总结
1. 本次实验实现了将文章编码、解码的功能,但存放01代码时没有逐个bit存储,而是把’0’’1’当做一个字符,这一点做得不是很好;
2. 独立编写了哈夫曼树类,编写时虽然思路清楚,但很容易出现问题,比如弄错下标等等,编程能力还需进一步提高。
具体代码:
#include"utility.h"
#include"huffman_tree.h"
//#include"huffman_tree_node.h"
//#include"node.h"
//#include"string.h"
#include "lk_list.h"
void sort(char str[],int a[]) //统计字符频率
{int t;
for(int i=0;i<strlen(str);i++)
if(96<str[i]&&str[i]<123) //数组a的前0-25位放'a'至'z'频率
{t=str[i]-'a';
a[t]++;
}
else if(str[i]==' ') //数组a的第26位放空格符频率
{a[26]++;
}
else if(64<str[i]&&str[i]<91) //数组a的27-52位放'A'-'Z'频率
{t=str[i]-'A'+27;
a[t]++;}
else if(str[i]==',')
{a[53]++;}
else if(str[i]=='.')
{a[54]++;}
else if(str[i]=='?')
{a[55]++;}
else if(str[i]=='(')
{a[56]++;}
else if(str[i]==')')
{a[57]++;}
else if(str[i]=='-')
{a[58]++;}
else if(str[i]=='\'')
{a[59]++;}
else cout<<"遇到无法识别的字符!"<<endl;
}
void main()
{char str[]="abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ,.?()!\'";
int a[60]={0},i=0;
char b[3000];
ifstream infile("text.txt");
infile.getline(b,3000);
infile.close();
sort(b,a); //得到权值表
HuffmanTree<char,int> tree(str,a,60);
String tcode;
const char *p;
ofstream outfile("tcode.txt");
for(i=0;i<strlen(b);i++)
{tcode=tree.Encode(b[i]);
p=tcode.CStr();
outfile<<p;
}
outfile.close(); //将转码存入"tcode.txt"
ofstream outfile2("mcode.txt");
outfile2<<str;
for(i=0;i<60;i++)
{outfile2<<a[i]<<'\t';
}
outfile2.close();//将字符及频率存入编码表,便于解码时生成相应的哈夫曼树
//以下为解码操作
char list[61];
int listf[60];
LinkList<char> texth;
ifstream infile2("mcode.txt");
infile2.getline(list,61);
for(i=0;i<60;i++)
{infile2>>listf[i];
}
infile2.close();
HuffmanTree<char,int> T(list,listf,60);
//cout<<list<<endl;
char t1[10000];
ifstream infile3("tcode.txt");
infile3.getline(t1,9999);
infile3.close();
const char *t2;
t2=t1;
String ff(t2);
texth=T.Decode(ff);
ofstream outfile3("testnew.txt"); //将解出的字符写入testnew.txt
char tt;
for(int j=1;j<texth.Length()+1;j++)
{texth.GetElem(j,tt);
outfile3<<tt;
}
outfile3.close();
}