enLZW LZW符号 deLZW LZW符号からの復号
LZW符号 long enLZW(unsigned char *code, long clen, unsigned char *text, long tlen); LZW符号からの復号 long deLZW(unsigned char *text, long tlen, unsigned char *code, long clen);
LZW符号関数において code (入出力)LZW符号を格納する配列 clen (入力) 配列codeの長さ text (入力) テキストを格納する配列 tlen (入力) 配列textの長さ LZW符号からの復号関数において text (入出力)テキストを格納する配列 tlen (入力) 配列textの長さ code (入力) LZW符号を格納する配列 clen (入力) 配列codeの長さ
LZW符号関数について LZW符号の長さ。符号化に失敗したときは -1L。 LZW符号からの復号関数について テキストの長さ。復号に失敗したときは -1L。
#define CHAR_SIZE 256 #define NODE_SIZE 4096 #define BITS_LEN 12 /* 2^^12 = 4096 */ typedef struct { unsigned char chr; /* 文字 */ int parent; /* 親ノードへ */ int brother; /* 兄弟ノードへ */ int child; /* 子ノードへ */ } NODE; long enLZW(unsigned char *code, long clen, unsigned char *text, long tlen) { int i, j; int w, k, rv; long c, len; int freeNode; NODE node[NODE_SIZE]; static int wMask[BITS_LEN] = { 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1}; static unsigned char bit0[8] = {~128,~64,~32,~16,~8,~4,~2,~1}; static unsigned char bit1[8] = { 128, 64, 32, 16, 8, 4, 2, 1}; if (tlen <= 0) return -1; for (i = 0; i <= CHAR_SIZE; i++) { node[i].chr = (unsigned char)i; node[i].brother = i+1; node[i].parent = node[i].child = 0; } node[CHAR_SIZE].brother = 0; freeNode = CHAR_SIZE + 1; /* 符号化と木の更新 */ w = text[0]; for (c = 1, i = 0, len = 0; ; c++) { k = (c >= tlen) ? CHAR_SIZE : text[c]; /* wk が登録済 ? */ for (rv = node[w].child; rv > 0; rv = node[rv].brother) if (node[rv].chr == k) break; if (rv > 0) w = rv; else { /* 参照番号を出力 */ for (j = 0; j < BITS_LEN; j++) { if (w & wMask[j]) *code |= bit1[i]; else *code &= bit0[i]; if (++i == 8) { code++; if (++len > clen) return -1L; i = 0; } } /* wk を登録 */ if (freeNode < NODE_SIZE) { node[freeNode].parent = w; node[freeNode].chr = k; node[freeNode].brother = node[w].child; node[freeNode].child = 0; node[w].child = freeNode++; } w = k; } if (c == tlen+1) break; /* EOF */ } if (i > 0) len++; return (len > clen) ? -1L : len; } long deLZW(unsigned char *text, long tlen, unsigned char *code, long clen) { int i, j; int w, k, rv; long c, len; int freeNode; NODE node[NODE_SIZE]; int stack[NODE_SIZE]; int sp; static int wMask[BITS_LEN] = { 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1}; static unsigned char bit1[8] = { 128, 64, 32, 16, 8, 4, 2, 1}; if (clen <= 0) return -1; for (i = 0; i <= CHAR_SIZE; i++) { node[i].chr = (unsigned char)i; node[i].brother = i+1; node[i].parent = node[i].child = 0; } node[CHAR_SIZE].brother = 0; freeNode = CHAR_SIZE + 1; /* 復号化と木の更新 */ for (i = 0, len = 0, c = 0, sp = 0; ; ) { for (j = 0, rv = 0; j < BITS_LEN; j++) { if (*code & bit1[i]) rv |= wMask[j]; if (++i == 8) { code++; if (++c > clen) return -1L; i = 0; } } if (rv == CHAR_SIZE) break; /* EOF */ /* 参照番号に対応するノードからルートまで登りながら文字を出力 */ if (rv >= freeNode) { stack[sp++] = k; /* 例外処理 */ k = w; } else k = rv; while (k > CHAR_SIZE) { stack[sp++] = node[k].chr; k = node[k].parent; } stack[sp++] = k; while (sp) { if (++len > tlen) return -1L; *text++ = stack[--sp]; } /* wk を登録 */ if (len > 1 && freeNode < NODE_SIZE) { node[freeNode].parent = w; node[freeNode].chr = k; node[freeNode].brother = node[w].child; node[freeNode].child = 0; node[w].child = freeNode++; } w = rv; } return len; }
LZ78符号は、辞書としてこれまでに見いだした部分文字列の一覧表を使うこ とで、大域的な辞書の作成が可能となる。実際には、入力された文字列を増 分分解と呼ばれる方法で部分文字列に分解し、得られた部分文字列を辞書に 登録し、この辞書を利用して符号化を行っている。
LZ78符号では、符号語中に必ず入力文字列中の文字そのものが含まれている が、LZW符号には、1文字からなる部分文字列をすべて辞書に前もって登録し ておくことで、符号語中に含まれる文字を取り除き、つねに辞書の参照番号 のみで済ませる。
具体的には、LZW符号は入力テキストにある部分文字列に参照番号を割り振っ た辞書をつくり、再びその部分文字列が現れたらその参照番号を符号語とし て出力する。部分文字列の照合は最長一致法で行い、新たに登録する部分文 字列は以前に登録したものを1文字延長したものである。初期辞書は入力に あるすべての文字に参照番号を割り振ったものとする。
LZW符号では、符号語を以下のようにして構成する。
LZW符号の各種改良版を実際に使った圧縮プログラムとしては、MS-DOSにお いては PKARC、UNIX の compress コマンド、Macintosh の StfuuIt などが あり、コンピュータやOSの種類を問わず、広く利用されている。また、ホー ムページに広く利用されている GIFイメージファイルに、圧縮アルゴリズム として採用されている。