正儿八经的数据压缩小课堂——从C++看词典编码之LZW编码
前言
什么是编码?我们为什么要编码?要理解这个听上去很高大上的玩意儿其实并不困难——
简单地说,编码是人类利用信息对象(比如数据)的统计特性等特性,来尽可能地压缩对象在传输过程中的体量!以此,来实现本来不可能实现的信息传输过程,还可以节约成本!爽!
今天我们要了解的LZW编码,属于编码大家庭中,词典编码的一种。
一、词典编码
1 通用编码的概念
通用编码的实用性更强、范围更广——管你是什么糟心的实际应用情境,我都能统统“通用”!
许多场合无法观察信源的统计特性,或者可观察但统计特性有变化。对这些数据进行压缩编码的技术统称为通用编码技术。
2 词典编码的思路
常见的数据文件,比如文本、图像、视音频、源代码中,通常都含有大量的重复字符。如果用一些简单的代号代替这些字符串,就可以实现压缩!
字符串与代号的对应表,就是词典。
LZW初始会有一个默认的字典,包含了所有256个8bit字符,单个字符的记号就是它自身,用数字表示就是ASCII值。在此基础上,编码过程中加入的新的记号的映射,从256开始,称为扩展表(Extended Dictionary)。↓
不管词典里单个词条中的字符串有多长,最后对应(映射)的都是一个定长的码字!
3 词典解码的思路
LZW编码与LZ77、LZSS等其他传统词典编码的最大区别就在于,这种方法在解码时,解码端会仿照编码端进行编码时的过程,自行生成一本“解码词典”。
重点来了各位,在上图的例子中,当解码进行到259(解码端收到259时),解码端是懵逼的。为什么?当前解码端的字典里还没有编到259这一项!是的,解码端总是比编码端“延后”一步!
那么怎么办呢?不用担心,首先,我们只要逆向思考一下就会知道,什么时候会出现这种情况——那就是在编码端,当一个词条被录入后,它在下一步里马上就被用到!
比如例子里的:
- pw1 ab cw1 a 此时aba录入词典,成为259号词条。
- pw2 a cw2 b 此时ab在词典里,继续读下一个字符:pw3 ab cw3 a 此时aba也在词典里!刚刚写进去的,所以这时编码端会直接出一个259给解码端!
通过以上进程,这个问题就好办了!在解码端,我们将会知道这个所谓259号的“未知词条”,它作为一个刚建立就马上被用到的词条,它一定是等于(前一个词条+前一个词条的首字符)!
259:aba = 256:ab + a
为啥捏?看我慢慢道来:
——因为“马上被调用”,所以259是衔接在256尾巴后面的,
ab aba
它的(作为词条被首次正式引用时的)头部的首字符
ab (a)ba
等于(作为词条首次出现被编入词典时的,注意,这和上面不一样!)的尾部的最后一个字符,
ab(a) ba
(其实就是这个a从cw1移动到pw2了哈哈)也就是说,259是个回文形式!(1)
——又因为259的建立就是=前一个词条256的整个词条+下一个单字符的码字(不用纠结,新建词条的最后一个码字一定是一个单字符!)(2)
那么由(1)+(2)可得,这条新建的词条259,就等于(前一个词条+前一个词条的首字符)!
简单版:未知词条=前一个传过来的词条+它的首字符,完事儿。
二、代码注释
/*
* Definition for LZW coding
*
* vim: ts=4 sw=4 cindent nowrap
*/
#include <stdlib.h>
#include <stdio.h>
#include "bitio.h"
#define MAX_CODE 65535
#pragma warning(disable:4996)
struct {
int suffix;
int parent, firstchild, nextsibling;
} dictionary[MAX_CODE+1];
int next_code;
int d_stack[MAX_CODE]; // stack for decoding a phrase
#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);
printf( "%4d->", n);
while( 0<count--) printf("%c", (char)(d_stack[count]));
printf( "\n");
}
printf("\n");
}
/*打印出自定义的字典*/