C++工程设计:字符编码器,实现对char型文本数据的编码,解码,压缩等操作。
一,结构设计:(箭头表示继承关系)
二,工具类设计:
CFileName,CHufmanheap,(Allocate)(为泛型空间配置器),(CBiset)(二进制,编码信号转换类),(CStack),CodeTree HufmanTree,(客服端类)等。
三,框架说明:
CCode类 :
架构中的公共基类,内含字符编码表,可完成统计要编码字符数,销毁编码表等基本操作,不提供外部接口,只提供子类接口和子类端口。
CEncode类:
继承自CCode,内包含字符统计数组,可完成编码前的对文本的各字符出现的次数,文本的长度等基本操作,提供三个输入,一个输出和更改文件路径的外部接口。
CTranslate类:
继承自CCode,完成读入编码表、按编码表对文本进行编码等操作。
CDecode类:
继承自CCode,完成读入编码表和已编码的文本,确认该文本的编码方式等操作。
(CMorencode)类:
继承自CEncode,完成文本的Mor编码,生成对应的编码表。
CHufmanencdoe类:
继承自CEncode,完成文本的hufman编码,生成对应的编码表。
(CMordecode)和CHufmandecode类:
继承自CDecode,完成对应的解码过程。
四,工具类说明:
CFilename类:
文件名类,提供对文件名操作方法,分离文件名和文件路径,分离文件名与文件扩展名等操作。
CHufmanheap类:
关于HufmanNode中数据频数的小顶堆,用于生成哈弗曼编码树,为CHufmanencode的内建类,禁止外界调用。
(CBiset)类:
专门用于处理‘0’、‘1’二进制字符,如转换成bit的数据,方便文件的压缩处理,或者转化成输出信号,二进制编码处理类。
CodeTree,HufmanTree:
为struct,HufmanTree继承自CodeTree,HufmanTree用于生成Hufman编码必须的过程,CodeTree用于解码。
(客服端类):
封装了所有可能客服端操作,定义提供对应的消息信号。
五,主要数据结构:
堆栈,小顶堆,二叉树,单链表等
六,主要过程:
(1),编码过程(Hufmanencode,共三个外部接口):
编码过程,先调用CEncode类里的inputchar(有三个版本,一个是读入一个字符,一个是读入一段话,一个是按CEncode里的Filename所对应的文本读入文件),调用inputchar函数读入完成后,会自动调用count函数,将文本信息统计入_Numofchar表中,还有一个Filelength来统计文本长度里。第一步完成后,调用CHufmanencode类里的encode函数进行编码,encode函数调用CreateHufmanTree函数生成HufmanTree,再调用TreetoList函数生成对应字符编码到CCode::CodeList里面去,完成编码。最后由CEncode::Outputchar函数输出编码到.cde文件里。编码过程只提供这三个外部接口。
(2),对文本的translate:由CTranslate完成,分为读入编码,读入字符输出对应的编码,生成.huf文件。
(3),解码过程(Hufmandecode,提供三个外部接口):
解码过程,先调用CDecode::inputchar(),按其Filename读入.cde文
件,将其中信息储存到CodeList里,然后调用CHufamandecode::CreatecodeTree生成CodeTree,然后用Outputchar函数,对人Filename的.huf文件,按读入字符遍历CodeTree,输出字符。
七,关键步骤:
(1),生成哈弗曼树的过程:
|
Q = (HufmanTree)malloc(sizeof(HufmanNode))
Q->p = P1->p + P2->p;
代码片段:
CreateNode();
while(true)
{
P1 = heap.pop();
if(heap.isempty()) break;
P2 = heap.pop();
Q = (HufmanTree)malloc(sizeof(HufmanNode));
Q->pchar =(char)NULCODE;
Q->code = NULL;
Q->lchild = P1;
Q->rchild = P2;
Q->p = P1->p + P2->p;
heap.push(h);
}
head= P1;
文字说明:
生成哈弗曼树的过程是这样的,首先将读入的字符和字符频数生成hufman结点,并一一压入到小顶堆heap中。然后进入无限循环,利用小顶堆特性不论用户按什么顺序输入,pop输出的永远是堆中最小的项(比较由频数进行),进行两次pop操作,压出p1,p2,建立一个新节点Q,让这个结点代表的字符为NULCODE(内定义的宏),它的频数为p1->p+p2->p,lchild和rchild分别指向p1和p2,然后将Q压入小顶堆,重复上面的操作,当heap为空时,终止循环,最后出堆的结点即为HufmanTree的头结点。
(2),解码过程
示例:
首先根据读入编码表生成CodeTree,然后一个一个字符地读入.huf文件,读入过程伴随一个CodeNode指针lp运动,初始始为lp= head,如果读入为‘0’则 lp=lp->lchild,为‘1’则lp=lp->rchild,这时判断lp->pchar是否为NULCODE,如果不为,则输出lp->pchar且lp=head,用这个完成编码。如:图中树,读入”000”则输出’a’。
代码片段:
while((c=fgetc(fp1))!=EOF)
{
p = Next(p,c);
if((p->pchar)!=(char)NULCODE)
{
fputc(p->pchar,fp2);
p = head;
}
}
八,重要底层实现代码:
(1),内定义宏:
#define MAX_CODE 128 //由于是char型字符,故最多为128个。
#define NULCODE 0xff //表示无字符,用于填充Hufman树和Code树。
#define LEFT true //把向左定义为bool型的true。
#define RIGHT false //把向右定义为bool型的false。
(2),heap堆实现机制:用数组表示二叉搜索树的结构实现
比较大小方式在HufmanNode里定义:
int operator >(HufmanNode& a)
{
returnp>a.p;
}
内定义数据:
HufmanTreeNodeList[MAX_CODE]; //Hufman堆数组实现
int top; //堆顶指针
Push操作:
void CHufmanheap::push(HufmanTree a)
{
top++;
push_heap(a,top); //转调用内部函数push_heap函数完成
}
Pop操作:
HufmanTree CHufmanheap::pop()
{
HufmanTreeresult = NodeList[1];
adjust_heap();
return result; //返回堆定元素,并调用内部函数调整堆。
}
内部函数push_heap:
void CHufmanheap::push_heap(HufmanTree a,int st)
{
NodeList[st]= a; //先将元素插入到st的位置
HufmanTreevalue = a; //用于过程中的判断
intholeIndex = st; //当前节点的位置
intparent = holeIndex / 2; //找其父节点
//判断父节点是否满足小顶堆条件,不满足则交换其和当前节点
//位置,直到满足位置
while(holeIndex> 1 && *NodeList[parent] > *value)
{
NodeList[holeIndex]= NodeList[parent];
holeIndex= parent;
parent= holeIndex / 2;
}
NodeList[holeIndex]= a;
}
内部函数adjust_heap:
voidCHufmanheap::adjust_heap()
{
intholeIndex = 1; //当前位置,从头节点开始调整
intsecondChild = 2 * holeIndex + 1; //找到右子节点
HufmanTreevalue = NodeList[top];
//递归填空直到满足小顶堆的条件
while(secondChild<top)
{
if(*NodeList[secondChild]> *NodeList[secondChild -1])
{
secondChild--;
}
NodeList[holeIndex]= NodeList[secondChild];
holeIndex= secondChild;
secondChild= 2 * holeIndex + 1;
}
if(secondChild== top)
{
NodeList[holeIndex]= NodeList[secondChild - 1];
holeIndex= secondChild - 1;
}
push_heap(value,holeIndex);//将末尾的值重新压入到合适的位置
NodeList[top]= NULL;
top--;
}
(3),CDecode读入编码后生成CodeTree的过程:
CreateTree函数:
voidCHufmanDecode::CreateTree()
{
inti;
for(i=0;i<MAX_CODE;i++)
{
if(CodeList[i])
{
CreateNode((char)i,CodeList[i]);
}
}
}
CreateNode函数:
voidCHufmanDecode::CreateNode(char a, char *p)
{
char*q = p;
CodeTreex,y;
x =head;
while(*(q+1))
{
if(*q == '0')
{
if(x->lchild)
{
x = x->lchild;
}
else
{
y = (CodeTree) malloc(sizeof(CodeNode));
y ->lchild =NULL;
y ->rchild =NULL;
y ->pchar =(char)NULCODE;
x ->lchild = y;
x = x->lchild;
}
}
elseif(*q == '1')
{
if(x->rchild)
{
x = x->rchild;
}
else
{
y = (CodeTree) malloc(sizeof(CodeNode));
y ->lchild =NULL;
y ->rchild =NULL;
y ->pchar =(char)NULCODE;
x ->rchild = y;
x = x->rchild;
}
}
q++;
}
y =(CodeTree) malloc(sizeof(CodeNode));
y->lchild =NULL;
y->rchild =NULL;
y->pchar = a;
if(*q=='0')
{
x ->lchild = y;
}
elseif(*q == '1')
{
x ->rchild = y;
}
}
(4),工具类CFilename:
调整文件扩展名函数:
void CFilename::AdjFilename()
{
char*p = Filename;
while(*p)
{
if(*p=='.')
break;
p++;
}
if(*p)
{
*p= '\0';
}
}
注:文中带括号的内容均为第一次完成后的改进,因其在实验要求之外故不作说明,其余类内部细节见源码附件。