目录
一、实验目的
掌握词典编码的基本原理,用C/C++/Python等语言编程实现LZW解码器并分析编解码算
法。
二、实验原理
1. LZW编码原理
① 将词典初始化为包含所有可能的单字符,当前前缀P初始化为空。
② 当前字符C=字符流中的下一个字符。
③ 判断P+C是否在词典中
1° 如果“是”,则用C扩展P,即让P=P+C,返回到步骤2。
2° 如果“否”,则输出与当前前缀P相对应的码字W;
将P+C添加到词典中;
令P=C,并返回到步骤2。
2. LZW解码原理
① 在开始译码时词典包含所有可能的前缀根。
② 令CW:=码字流中的第一个码字。
③ 输出当前缀-符串string.CW到码字流。
④ 先前码字PW:=当前码字CW。
⑤ 当前码字CW:=码字流的下一个码字。
⑥ 判断当前缀-符串string.CW 是否在词典中。
1° 如果”是”,则把当前缀-符串string.CW输出到字符流;
当前前缀P:=先前缀-符串string.PW;
当前字符C:=当前前缀-符串string.CW的第一个字符;
把缀-符串P+C添加到词典。
2° 如果”否”,则当前前缀P:=先前缀-符串string.PW;
当前字符C:=当前缀-符串string.CW的第一个字符;
输出缀-符串P+C到字符流,然后把它添加到词典中。
三、实验代码
1. 数据结构分析
struct {
int suffix;//尾缀字符
int parent, firstchild, nextsibling;//母节点,第一个孩子节点,下一个兄弟节点
} dictionary[MAX_CODE+1];
int next_code;
int d_stack[MAX_CODE]; // stack for decoding a phrase
2. 主要功能模块
(1)初始化词典
void InitDictionary(void) {//将0~255根节点初始化
int i;
for (i = 0; i < 256; i++) {
dictionary[i].suffix = i;
dictionary[i].parent = -1;//母节点初始化为空
dictionary[i].firstchild = -1;//子节点初始化为空
dictionary[i].nextsibling = i + 1;
}
dictionary[255].nextsibling = -1;
next_code = 256;//新词条从256开始编码
}
(2)将新串加入词典
void AddToDictionary(int character, int string_code) {
int firstsibling, nextsibling;
if (0 > string_code) return;//当前词条无前缀,为单个字符,已存在词典中
dictionary[next_code].suffix = character;
dictionary[next_code].parent = string_code;
dictionary[next_code].nextsibling = -1;
dictionary[next_code].firstchild = -1;
firstsibling = dictionary[string_code].firstchild;//当前前缀的第一个孩子
if (-1 < firstsibling) { // 如果当前前缀有孩子
nextsibling = firstsibling;
while (-1 < dictionary[nextsibling].nextsibling) //只要nextsibling还有下一个兄弟
nextsibling = dictionary[nextsibling].nextsibling;
dictionary[nextsibling].nextsibling = next_code;
}
else {// 当前前缀无孩子,则新节点为它的第一个孩子
dictionary[string_code].firstchild = next_code;
}
next_code++;
}
(3)查找词典中是否有字符串
int InDictionary( int character, int string_code){
int sibling;
if( 0>string_code) return character;//当前词条无前缀,为单个字符,初始化后已经在词典中,返回此字符
sibling = dictionary[string_code].firstchild;//如果不是单个字符,找当前前缀的第一个孩子节点
while( -1<sibling){
if( character == dictionary[sibling].suffix) return sibling;//如果此孩子节点的尾缀字符等于character,则当前词条在词典中,返回此孩子节点
sibling = dictionary[sibling].nextsibling;//否则,找当前前缀的下一个孩子节点
}
return -1;//没有找到,返回-1
}
(4)编码
void LZWEncode( FILE *fp, BITFILE *bf){
int character;//当前字符C
int string_code;//前缀P
int index;//索引
unsigned long file_length;//文件长度
fseek( fp, 0, SEEK_END);//文件指针置于文件末尾
file_length = ftell( fp);//获取文件长度
fseek( fp, 0, SEEK_SET);//文件指针置于文件头
BitsOutput( bf, file_length, 4*8);
InitDictionary();//初始化词典
string_code = -1;
while( EOF!=(character=fgetc( fp))){
index = InD