LZW压缩算法

3/8/2017来源:ASP.NET技巧人气:2154

1.LZW压缩适用的场景:      对于有大量重复出现的字符或者字符串,可以将重复出现的多个串用一个字符表示,并写进文件。比如可以压缩这个字符串:“TOBEORNOTTOBEORTOBEORNOT ”,这个字符串中出现了大量的TO,BE,OR,NOT等等字符,我们就可以将这些重复出现的字符压缩写进文件。这里只是说明算法,并没有涉及写进文件的操作。 2.压缩和解压缩的原理: (1)压缩:这里定义一个字典,字典里存放字符或者字符串的编码。一开始字典被初始化为: A:1 B:2 C:3 D:4 ..... Y:25 Z:26 之后,出现字母T,我们就将20(ASCII中20对应的字符)写进压缩文件,然后将TO写进字典并编号27.下一个读到字符O,将15写进压缩文件,OB写进字典等等。所以,字典就会被扩充。 TO:27 OB:28 BE:29 EO:30 等等。 所以,压缩的时候,只需要将出现的字符或者字符串(有在字典中出现过)的编码写进压缩文件即可,(比如,TO在字典中出现过,我们就将TO的编码写进文件,不会单独记录T和O),并将当前的字符或者字符串与下一个字符组成新的串写进字典。 (2)解压缩:用编码找到对应的字符或者字符串,然后写进解压缩文件(比如,看到编码15,就会将T写入解压缩文件)。 3.代码实现:
#include<iostream>
using namespace std;
#include<string>
//LZW压缩和解压缩
typedef struct Dict
{
       string _s;
       int _code;
}Dict;
class ComPRess
{
public:
       Compress(int maxSize,string text)
       {
              _maxSize = maxSize;
              _code = new int[maxSize];
              _size = 0;
              _text = text;
              _dict = new Dict[_maxSize];
              _dictSize = 0;
              //过滤0号位置
              Insert("#");
              //向字典中插入26个字母
              string s = "A";
              for (int i = 0; i < 26; ++i)
              {
                     Insert(s);
                     s[0]++;
              }
       }
       ~Compress()
       {
              delete[] _code;
              delete[] _dict;
              _code = NULL;
              _dict = NULL;
              _maxSize = 0;
              _size = 0;
       }
       string Encode()
       {
              string s;//存储压缩后的字符串
              string cur;
              char next;
              int i = 0;
              while (i < _text.size())
              {
                     cur.insert(cur.size(),1, _text[i]);
                     i++;
                     next = _text[i];
                     while (FindCode(cur) != -1)//当前串在字典中
                     {      
                           cur.insert(cur.size(), 1, next);
                           ++i;
                           if (i != _text.size())
                                  next = _text[i];
                           else
                                  break;
                     }
                     if (i != _text.size())
                     {
                           char c = cur.at(cur.size() - 1);
                           cur.erase(cur.end()-1);
                           _code[_size] = FindCode(cur);
                           ++_size;
                           cur += c;
                           c =  _code[_size-1];
                           s.push_back(c);
                           Insert(cur);
                     }             
                     if(i == _text.size() && FindCode(cur) != -1)
                     {
                           _code[_size] = FindCode(cur);
                           s.push_back(_code[_size]);
                           break;
                     }             
                     --i;
                     cur.clear();
              }
              return s;
       }
       string Decode(const string& s)
       {
              string text;
              string cur;
              if (s.size() <= 0)
                     return s;
              int code;
              for (int i = 0; i < s.size(); ++i)
              {
                     code = (int)(s[i]);
                     cur = FindSeq(code);
                     text += cur;
              }
              return text;
       }
       void PrintDict()
       {
              cout<<"seq  code"<<endl;
              for (int i = 0; i < _dictSize; ++i)
              {
                     cout << _dict[i]._s << "     " << _dict[i]._code<<endl;
              }
       }
protected:
       int FindCode(const string& s)
       {
              for (int i = 0; i < _dictSize; ++i)
              {
                     if (s == _dict[i]._s)
                           return i;
              }
              return -1;
       }
       string FindSeq(int code)
       {
              return _dict[code]._s;
       }
       void Insert(string seq)
       {
              _dict[_dictSize]._s = seq;
              _dict[_dictSize]._code = _dictSize;
              _dictSize++;
       }
private:
       Dict* _dict;
       int _dictSize;
       int* _code;
       int _size;
       int _maxSize;
       string _text;
};
int main()
{
       string text;
       cin >> text;
       Compress c(100,text);
       //c.PrintDict();
       string s = c.Encode();
     //  c.PrintDict();
       //cout << s;
       cout<<c.Decode(s)<<endl;
       system("pause");
       return 0;
}

代码运行结果:








当然了,要实现真正的压缩,就要把压缩信息写进文件。如果要将压缩和解压缩进行分离的话,还需要在解压缩的时候,完成一次字典的填充工作。
这里只是为了说明算法而已。