实验目的:
掌握词典编码的基本原理,编程实现LZW解码器并分析编解码算法。
实验原理:
实验过程:
#include <stdlib.h>
#include <stdio.h>
#include “bitio.h”
#define MAX_CODE 65535
struct {
int suffix;
int parent, firstchild, nextsibling;
} dictionary[MAX_CODE+1];
int next_code;
int d_stack[MAX_CODE];
#define input(f) ((int)BitsInput( f, 16))
#define output(f, x) BitsOutput( f, (unsigned long)(x), 16)
int DecodeString( int start, int code);
void InitDictionary( void);
void PrintDictionary( void){
int n;
int count;
for( n=256; n<next_code; n++){
count = DecodeString( 0, n);//count为所在层数(即该词条码元个数)
printf( “%4d->”, n);
while( 0<count–) printf("%c", (char)(d_stack[count]));
printf( “\n”);
}
}
int DecodeString( int start, int code){ //可找到上层母结点层数
int count;
count = start;
while( 0<=code){
d_stack[ count] = dictionary[code].suffix;//按照从叶向根的顺序将码元写入dstack(即数组下标低的存叶结点的字符,则print函数中count–可按正序输出码字)
code = dictionary[code].parent;//从下层向上寻找母结点
count ++;
}
return count;
}
void InitDictionary( void){
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;
}
int InDictionary( int character, int string_code){//寻找string_code结点下值为character的孩子结点
int sibling;
if( 0>string_code) return character;
sibling = dictionary[string_code].firstchild;
while( -1<sibling){
if( character == dictionary[sibling].suffix) return sibling;
sibling = dictionary[sibling].nextsibling;
}
return -1;
}
void AddToDictionary( int character, int string_code){//将stringcode+charactor送入词典
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){ // the parent has child
nextsibling = firstsibling;
while( -1<dictionary[nextsibling].nextsibling )
nextsibling = dictionary[nextsibling].nextsibling;
dictionary[nextsibling].nextsibling = next_code;
}else{// no child before, modify it to be the first
dictionary[string_code].firstchild = next_code;//string_code有孩子结点时,将next_code设置为string_code的最末一个孩子结点,若string_code没有孩子结点则将next_code设置为string_code的第一个孩子结点
}
next_code ++;
}
void LZWEncode( FILE *fp, BITFILE *bf){
int character;
int string_code;
int index;
unsigned long file_length;
fseek( fp, 0, SEEK_END);
//int fseek(FILE *stream,ling offset,int fromwhere)stream将指向以fromwhere为基准,偏移offset个字节的位置
file_length = ftell( fp);
fseek( fp, 0, SEEK_SET);
BitsOutput( bf, file_length, 4*8);//4*8为一个int字节的bit数,输出bf指向文件,每次输出一个int,输出filelength次
InitDictionary();
string_code = -1;
while( EOF!=(character=fgetc( fp))){
index = InDictionary( character, string_code);//stringcode代表前缀串p,找p+c是否在词典中
if( 0<=index){ // string+character in dictionary
string_code = index;//将p+c送给p
}else{ // string+character not in dictionary
output( bf, string_code);//输出p对应码字
if( MAX_CODE > next_code){ // free space in dictionary
// add string+character to dictionary
AddToDictionary( character, string_code);//将p+c送入词典
}
string_code = character;//将c作为新前缀p
}
}
output( bf, string_code);//用来输出最后一个字符,如:abac,aba为词典中的字条,则执行操作时会将aba对应索引号输出,然后将abac作为新词条存入词典中,此时,若abac为结尾,则需将c单独编码输出
}
void LZWDecode( BITFILE *bf, FILE *fp){
int character;
int new_code, last_code;
int phrase_length;
unsigned long file_length;
file_length = BitsInput( bf, 4*8);
if( -1 == file_length) file_length = 0;
/*需填充*/
InitDictionary();
last_code=-1;
while(file_length>0)
{
if(last_code<0)
{
new_code=input(bf);
phrase_length=DecodeString(0,new_code);
}
else
{
new_code=input(bf);
if(new_code<next_code)//该码字在词典中
{
phrase_length=DecodeString(0,new_code);
character=d_stack[phrase_length-1];
if(MAX_CODE>next_code){AddToDictionary(character,last_code);}
}
else//该码字不在词典中
{
phrase_length=DecodeString(1,last_code);//预留出下标1的位置存放lastcode的首字符,由于DecodeString中count从1开始计数,故phraselength比lastcode的长度多一
character=d_stack[phrase_length-1];
if(MAX_CODE>next_code){AddToDictionary(character,last_code);}
d_stack[0]=character;
}
}
//输出当前码字
while( 0<phrase_length)
{
phrase_length --;
fputc( d_stack[ phrase_length], fp);
file_length--;
}
last_code=new_code;
}
}
int main( int argc, char **argv){
FILE *fp;
BITFILE *bf;
if( 4>argc){
fprintf( stdout, "usage: \n%s <o> <ifile> <ofile>\n", argv[0]);
fprintf( stdout, "\t<o>: E or D reffers encode or decode\n");
fprintf( stdout, "\t<ifile>: input file name\n");
fprintf( stdout, "\t<ofile>: output file name\n");
return -1;
}
if( 'E' == argv[1][0]){ // do encoding
fp = fopen( argv[2], "rb");
bf = OpenBitFileOutput( argv[3]);
if( NULL!=fp && NULL!=bf){
LZWEncode( fp, bf);
fclose( fp);
CloseBitFileOutput( bf);
fprintf( stdout, "encoding done\n");
}
}else if( 'D' == argv[1][0]){ // do decoding
bf = OpenBitFileInput( argv[2]);
fp = fopen( argv[3], "wb");
if( NULL!=fp && NULL!=bf){
LZWDecode( bf, fp);
fclose( fp);
CloseBitFileInput( bf);
fprintf( stdout, "decoding done\n");
}
}else{ // otherwise
fprintf( stderr, "not supported operation\n");
}
return 0;
}
实验结果:
TXT
LZW编码较为费时