其他分享
首页 > 其他分享> > 实验三——LZW编解码

实验三——LZW编解码

作者:互联网

一.实验目的

掌握词典编码的基本原理,用C/C++/Python等语言编程实现LZW解码器并分析编解码算法。

二.实验原理

1.LZW编码:

2.LZW解码:

三.实验过程

1.给出的实验代码解析

(1)词典树的结构体

struct 
{
	int suffix;
	int parent, firstchild, nextsibling;
} dictionary[MAX_CODE+1];

每个词条由前缀串加一个新字符组成,suffix中存储该新字符对应的ASCII码值,parent、firstchild、nextsibling分别存储父亲结点、第一个孩子结点和下一个兄弟结点对应的字典结构体数组的数组下标。

(2)找出当前词条对应码字的函数

int d_stack[MAX_CODE]; // stack for decoding a phrase(存码字)
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;
}

词典中每个词条结构体中只存储码字的最后一个字符,故想找到该词条的完整码字,需不断向上寻找母结点直到找到根结点。从code中存储的字符开始,依次上寻到全部的母结点存入d_stack数组中的连续内存空间中,即d_stack[]中存储了倒序的码字。注意:d_stack是从数组下标为start的位置开始存储该词条对应的码字的,函数的返回值为该词条对应码字的长度。

(3)输出词典函数

int next_code;
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");
	}
}

next_code存储词典中尚未被使用且即将被使用的数组下标(即下一个被存入的词条存入到next_code中)

(4)寻找孩子结点函数

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;
		sibling = dictionary[sibling].nextsibling;
	}
	return -1;
}

寻找string_code下suffix为character的孩子结点,返回该孩子结点对应的数组下标。该函数用于已知前缀字符串,查找前缀字符串加新字符是否在词典中。

(5)将新词条写入词典函数

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 ++;
}

(6)编码函数

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);
}

2.实验要求补充的解码函数

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;
	}

}

四.实验结果

1.txt文件编解码结果

2.docx文件编解码结果

3.十种不同格式文件压缩效率对比

文件格式压缩率
docx文件59.1%
tga文件165.7%
gif文件87.8%
xlsx文件56.3
pdf文件131.9%
pptx文件143.7%
avi文件119.5%
png文件150.7%
jpg文件116.1%
txt文件90.7%

 

结果分析:并不是所有文件格式在经过编码后都能被压缩,有的反而会内容膨胀。

标签:编解码,code,string,int,character,LZW,length,实验,码字
来源: https://blog.csdn.net/weixin_52189060/article/details/115549294