C++实现LZ77压缩算法
LZ77算法是一种基于词典压缩的方法,并且该词典不是静态的,而是自适应的动态词典。LZ77把已经压缩过的数据当成词典,未压缩的数据在已经压缩的数据中查找(即动态词典),然后把偏移量和匹配长度表示出来的一种方法。
介绍LZ77原理的文章很多,这里就不啰嗦了。其实算法思路也是比较简单。本着"纸上得来终觉浅"的观念,我这里实现最基本的LZ77的压缩和解压算法,实际上出于压缩效率的考虑,LZ77的压缩算法存在很多的变种。但是理解变种之前,先理解最原始的思路更有帮助,而且知道来龙去脉。如下是一个例子的简单图解。
#include <Windows.h>
#include <stdio.h>
#include "MyLZ77.h"void TestMyLZ77()
{//为了方便查看,使用字符串测试,MyLZ77Encode函数是支持二进制压缩的char *pTestStr = "aacaacabcabaaac";int testStrLen = strlen(pTestStr);PLZ77_ENCODE_LIST header = MyLZ77Encode((unsigned char*)pTestStr,testStrLen);//最后一个字节看能会多出来,暂时未处理PLZ77_ENCODE_LIST p = header;if(header){while(p){printf("(%2d,%2d,%2c)\r\n",p->item->searchResult->pos,p->item->searchResult->matchLen,p->item->nextChar); p = p->next;}}else{printf("Error: MyLZ77Encode return NULL\r\n");}char decodeBuf[1024] = {'\0'};int decodeLen = MyLZ77Decode(header,(unsigned char*)decodeBuf); //最后一个字节看能会多出来,暂时未处理if(decodeLen != testStrLen){printf("Error:Decode len is not match data len before encode.May be add a byte in the end!!!\r\n");}if(strncmp(pTestStr,decodeBuf,testStrLen)){printf("Error:Decode data is not match data len before encode\r\n");}else{printf("Decode Success!!!\r\n");}
}int main(int agrc,char* argv[])
{TestMyLZ77();getchar();return 0;
}
和图解的例子一样,测试结果如下:
#ifndef _MYLZ77_H_
#define _MYLZ77_H_typedef struct _t_LZ77_ENCODE_SEARCH_RESULT
{_t_LZ77_ENCODE_SEARCH_RESULT(){pos = -1;matchLen = 0;}short pos;//字典匹配的起始位置,相对待编码数据的偏移short matchLen;//字典匹配的长度
}LZ77_ENCODE_SEARCH_RESULT,*PLZ77_ENCODE_SEARCH_RESULT;typedef struct _t_LZ77_ENCODE_ITEM
{PLZ77_ENCODE_SEARCH_RESULT searchResult;unsigned char nextChar;//匹配后的下一个未匹配的字符
}LZ77_ENCODE_ITEM,*PLZ77_ENCODE_ITEM;typedef struct _t_LZ77_ENCODE_LIST
{PLZ77_ENCODE_ITEM item;_t_LZ77_ENCODE_LIST* next;
}LZ77_ENCODE_LIST,*PLZ77_ENCODE_LIST;//标准的LZ77编码输出,这里使用链表保存编码的结果
PLZ77_ENCODE_LIST MyLZ77Encode(unsigned char* data,int dataLen);//标准的LZ77解码输出,输入需要编码后的链表,输出解码后的字节数
int MyLZ77Decode(PLZ77_ENCODE_LIST header,unsigned char *pDecodeOut);#endif
#include <Windows.h>
#include "MyLZ77.h"#define MY_LZ77_DICTIONARY_LEN 0x06 //动态字典的长度设置为6字节
#define MY_LZ77_ENCODE_BUFFER_LEN 0x04 //动态编码的buf长度设备为4字节//更新链表
PLZ77_ENCODE_LIST UpdataLZ77Link(PLZ77_ENCODE_LIST *lz77Header,PLZ77_ENCODE_LIST *pCur,PLZ77_ENCODE_ITEM item)
{PLZ77_ENCODE_LIST p = new LZ77_ENCODE_LIST;if(!p){return NULL;}p->item = item;p->next = NULL;if(!*pCur){*pCur = p;*lz77Header = p;}else{(*pCur)->next = p;*pCur = p;}return p;
}//通过m*n的时间复杂度搜索最长匹配,可以优化该函数
PLZ77_ENCODE_SEARCH_RESULT MyLZ77Search(unsigned char *pDictionary,unsigned short dictionarySize,unsigned char* pEncodeBuffer,unsigned short encodeBufferSize)
{PLZ77_ENCODE_SEARCH_RESULT searchResult =(PLZ77_ENCODE_SEARCH_RESULT)new LZ77_ENCODE_SEARCH_RESULT;if(!searchResult){return NULL;}//在字典中搜索for(int i=0;i<dictionarySize;i++){int pos = i;int index = i;for(int j=0;j<encodeBufferSize;j++){if(pEncodeBuffer[j] == pDictionary[index]){++index;continue;}else{break;}}int matchLen = index - i;if(matchLen && matchLen>searchResult->matchLen){searchResult->pos = dictionarySize-i;searchResult->matchLen = matchLen;}}return searchResult;
}//标准的LZ77编码输出,这里使用链表保存编码的结果
PLZ77_ENCODE_LIST MyLZ77Encode(unsigned char* data,int dataLen)
{if(!data || dataLen<=0){return NULL;}int hasbeenEncodeSize = 0;//已经编码的数据大小PLZ77_ENCODE_LIST lz77Header = NULL;PLZ77_ENCODE_LIST pCur = NULL;unsigned short dictionarySize = 0;//开始时字典长度为0unsigned short encodeBufferSize = MY_LZ77_ENCODE_BUFFER_LEN;if(dataLen<MY_LZ77_ENCODE_BUFFER_LEN){encodeBufferSize = dataLen;}unsigned char *pDictionary = data;unsigned char *pEncodeBuffer = data;while(encodeBufferSize>0){PLZ77_ENCODE_SEARCH_RESULT pResult = MyLZ77Search(pDictionary,dictionarySize,pEncodeBuffer,encodeBufferSize);if(!pResult){break;}PLZ77_ENCODE_ITEM item = new LZ77_ENCODE_ITEM;if(!item){break;}item->searchResult = pResult;item->nextChar = pEncodeBuffer[pResult->matchLen];hasbeenEncodeSize += (pResult->matchLen+1);//已编码的字节数同步增加//更新链表if(!UpdataLZ77Link(&lz77Header,&pCur,item)){break;}//更新动态字典dictionarySize += pResult->matchLen+1;if(dictionarySize>MY_LZ77_DICTIONARY_LEN){dictionarySize = MY_LZ77_DICTIONARY_LEN;pDictionary = (pEncodeBuffer+pResult->matchLen+1)-MY_LZ77_DICTIONARY_LEN;}//更新待编码的buffer指针,往后移动pEncodeBuffer += (pResult->matchLen+1);//未编码的数据已经不足MY_LZ77_ENCODE_BUFFER_LEN,更新未编码的数据,否则未编码的buffer大小仍然设置为MY_LZ77_ENCODE_BUFFER_LENif(dataLen-hasbeenEncodeSize<MY_LZ77_ENCODE_BUFFER_LEN){encodeBufferSize = ((dataLen-hasbeenEncodeSize)>=0)?(dataLen-hasbeenEncodeSize):0;}}return lz77Header;
}//标准的LZ77解码输出,输入需要编码后的链表,输出解码后的字节数
int MyLZ77Decode(PLZ77_ENCODE_LIST header,unsigned char *pDecodeOut)
{int decodeLen = 0;PLZ77_ENCODE_LIST p = header;unsigned char* pCursorData = pDecodeOut;while(p){ unsigned char *pCopy = pCursorData-p->item->searchResult->pos;for(int i=0;i<p->item->searchResult->matchLen;i++){*pCursorData = *pCopy;pCursorData++;pCopy++;}*pCursorData = p->item->nextChar;pCursorData++;decodeLen += (p->item->searchResult->matchLen+1);p = p->next;}return decodeLen;
}
这个例子可以看到,其实标准的原始LZ77很多时候压缩比率并不高。而在实际运用中,动态字典长度和待编码的长度都会比较长,这样压缩效果会更好。
我们修过如下:
#define MY_LZ77_DICTIONARY_LEN 32 //动态字典的长度设置为32字节
#define MY_LZ77_ENCODE_BUFFER_LEN 16 //动态编码的buf长度设备为16字节
测试字符串修过为:
char *pTestStr = "abababababababababababababababababababababababababababababab";
那么,测试结果为:
C++实现LZ77压缩算法相关推荐
- GZIP中的LZ77压缩算法
什么是LZ77压缩算法? ZIP中的LZ77思想 LZ77压缩和解压缩介绍 #ZIP: LZ77重复语句层面的压缩+huffman字节层面的压缩 #什么是LZ77压缩算法? 1977年由两个以色列人提 ...
- 数据流压缩原理实现(huffman编码,LZ77压缩算法)
1. 压缩原理deflate算法 a) LZ77 算法原理 b) Huffman算法原理 c) Huffman算法测试实例 2. 关于zlib库的实际应用以及gzip格式分析查看下一篇 一.数据压缩 ...
- LZ77压缩算法原理剖析
1. 数据压缩 数据压缩(Data Compression),简称为压缩,通常也被称为编码(coding).它是信息论的一个分支,其主要目的是使要传输的数据量尽量最小化.从根本上讲,它在保证能够包含原 ...
- Zlib压缩算法:LZ77、LZ78、霍夫曼编码、滑动窗口、Rabin-Karp算法、哈希链、I/O缓冲区
Table of Contents 1.简介 1.1 什么是zlib 2.压缩算法 2.1 放气 2.2 LZ77 2.2.1 滑动窗口 2.2.2 长距离对 2.3 霍夫曼编码 3. zlib的实现 ...
- LZ77文件压缩算法
LZ77压缩算法 1977由两个以色列人提出的基于重复语句层面的一种通用的压缩算法. 通用:对文件没有要求最终是将重复语句替换成更短的<长度,距离,先行缓冲区匹配字符串的下- -个字符>对 ...
- LZ77 压缩和解压缩
LZ77压缩算法编码原理详解(结合图片和简单代码) 转载自:https://www.cnblogs.com/junyuhuang/p/4138376.html 前言 LZ77算法是无损压缩算法,由以色 ...
- 数据压缩算法—2无损压缩算法
几个常见的编码算法 (一) 字典算法 字典算法是最为简单的压缩算法之一.它是把文本中出现频率比较多的单词或词汇组合做成一个对应的字典列表,并用特殊代码来表示这个单词或词汇.例如: 有字典列表: ...
- 基于GZIP压缩算法的模拟实现
ZIP压缩的历史 1977年,两位以色列人Jacob Ziv和Abraham Lempel,发表了一篇论文<A Universal Algorithm forSequential Data Co ...
- RLE行程长度压缩算法
RLE(Run Length Encoding)行程长度压缩算法(也称游程长度压缩算法),是最早出现.也是最简单的无损数据压缩算法.RLE算法的基本思路是把数据按照线性序列分成两种情况:一种是连续的重 ...
- 算法系列之八:RLE行程长度压缩算法
RLE(Run Length Encoding)行程长度压缩算法(也称游程长度压缩算法),是最早出现.也是最简单的无损数据压缩算法.RLE算法的基本思路是把数据按照线性序列分成两种情况:一种是连续的重 ...
最新文章
- 平滑迁移 Dubbo 服务的思考
- 【算法笔记】哈密顿问题
- arach Linux设置静态,大神面对win7系统archlinux静态网络配置错误造成无法上网的还原办法...
- Incorrect number of FETCH variables
- UNITY引擎变量调用产生不必要内存分配
- Mysql_mysql 性能分析及explain用法
- 天不知道地知道你不知道我知道谜底_温州这里有个7000平方米的“寻宝”地,你不知道就亏大了!...
- js网页文件资源加载器
- 桌面云计算: 提供灵活、可靠双保险
- Scala Case Class
- VS C++ string转int int转string
- paip.php调试不能显示局部变量内容w/ xdebug
- linux基础及网新运维,Linux运维工程师常用到的Linux基础命令(一)
- c语言程序设计总结与体会,c语言程序设计个人总结
- 3dsmax VRay分布式渲染
- 软件测试工程师必备技能
- 参照系(参考系)与参照物的区别
- 计算机分子模拟的意义包括,计算机分子模拟
- KindEditor 图片粘贴上传,实现图文粘贴,图片自动上传
- git使用及上传代码到github