LZW数据压缩算法

jopen 10年前

LZW算法常用于文本压缩中,尤其是对于重复出现的字符串的文本压缩效果不错。其主要思想是扫描文本,对每个出现的字符,检查其跟前向字符(串)是否能组成一个之前出现过的字符串,如果能那么继续向后扫描;如果不能,则将前向字符(串)转换为一个索引,并将索引写入输出文件中。这样就将文本都用其对应的索引代替写入输出文件中,如果文本中字符串重复越多,那么压缩效果就越好。

先给出代码:

    /********************************************************************           created:    2014/01/06 12:13           file base:  main           file ext:   cpp           author: lming_08@hotmail.com           purpose:    实现LZW算法,并测试其压缩效果       *********************************************************************/        #include <vector>        #include <iostream>        #include <fstream>        using namespace std;                #define READ_COUNT (8*1024)        #define RET_OK 0        #define RET_FAIL -1                typedef unsigned short UInt2;                typedef struct st_tb         {            char used;//标识该项是否被使用            unsigned int prev;//前向的下标            int ch;//当前的字符        }st_tb;        st_tb string_tab[4096] = {0};                int LZW(char *str, int strlen, ofstream *ofile);        int init_string_tab(st_tb *string_tab);        int search_frm_strtab(st_tb *string_tab, unsigned int prev, int ch);                int main(int argc, char *argv[])        {            char str[READ_COUNT] = {0};            int len = 0;            ifstream ifile;            streampos pos;            ofstream ofile;            int ret = 0;                    ifile.open(argv[1], ios_base::in);            ifile.read(str, READ_COUNT);            //计算输入文件的大小            ifile.seekg(0, ios::end);            pos = ifile.tellg();            cout<<"file "<<argv[1]<<", size is "<<pos<<"B"<<endl;            ifile.close();                    ofile.open(argv[2], ios_base::binary|ios_base::in);            len = strlen(str);            ret = LZW(str, len, &ofile);            ofile.seekp(0, ios::end);            pos = ofile.tellp();            cout<<"file "<<argv[2]<<", size is "<<pos<<"B"<<endl;            ofile.close();                    return 0;        }                int init_string_tab(st_tb *string_tab)        {            int i = 0;                    for (i = 0; i < 258; i++)            {                string_tab[i].used = 1;                string_tab[i].prev = -1;                string_tab[i].ch = i;            }            for(i = 258; i < 4096; i++)            {                string_tab[i].used = 0;            }            return 0;        }                int search_frm_strtab(st_tb *string_tab, unsigned int prev, int ch)        {            int i = 0;                    for (i = 258; i < 4096; i++)            {                if (string_tab[i].used)                {                    if (string_tab[i].prev == prev && string_tab[i].ch == ch)                    {                        return i;                    }                }            }            return RET_FAIL;        }                int LZW(char *str, int strlen, ofstream *ofile)        {            init_string_tab(string_tab);                    int i = 0;            int str_index = 258;            int index = 0;            vector<UInt2> uvec;            UInt2 uwPrev = str[0];                    uvec.push_back(256);//256通常用来表示开始新的编码表            for (i = 1; i < strlen; i++)            {                index = search_frm_strtab(string_tab, uwPrev, str[i]);                if (index == RET_FAIL)                {                    uvec.push_back(uwPrev);                            string_tab[str_index].used = 1;                    string_tab[str_index].prev = uwPrev;                    string_tab[str_index].ch = str[i];                            uwPrev = str[i];                    str_index++;                }                else                {                    uwPrev = index;                }            }            uvec.push_back(uwPrev);            uvec.push_back(257);                    //开始将转换后的数据写入文件中            int flag = 0;            char prevcode = 0;            /*            *  这里写入的数据格式举例如下,连续插入2个数分别为0x0ABC, 0x0abc,这样写入文件后的格式为BCAcab,            *每两个数都是以这种方式写入的            */            for (vector<UInt2>::iterator it = uvec.begin(); it != uvec.end(); it++)            {                UInt2 code = *it;                        if (flag == 0){                    char ch = code & 0xFF;                    ofile->write(&ch, 1);                    prevcode = (code >>4) & 0xF0;                    flag = 1;                }                 else{                    char ch = (code &0x0F) | prevcode;                    ofile->write(&ch, 1);                    flag = 0;                }            }            if (flag == 0){                char ch = prevcode & 0xF0;                ofile->write(&ch, 1);            }                    return RET_OK;        }