以前学习了一下GIF的LZW算法,不过只是学习了一下(见我以前的那篇博文《LZW for GIF算法原理和实现》),没有实践,也没有看看效果到底怎么样,因为现在ZIP库很多,基本上不需要自己写压缩算法了,LZW的压缩效果也比不上它们。不过最近有个嵌入式系统上的数据记录需求,希望把运行过程中采集的数据都记录下来,但是存储空间比较有限,处理器的能力也比较有限,是266兆的PPC,所以不能使用复杂的算法了,于是又想起LZW算法来,首先是采集的数据具有短时间内大量重复的特点,可以用简单的算法就达到很好的效果,其次这个算法处理得好的话很省资源,至于压缩比则是其次的考虑。
为了验证一下算法的正确性和效果,先用python实现了一下算法。由于只是验证算法,所以不考虑效率,只追求编程简单。用Python实现起来还真是方便,如果不进行位紧缩的话,二十多行代码就搞定了,拿几个文件试了下压缩和解压,一个140多K的cpp程序,可以压到33K多,用一个1兆多的软盘映像文件,可以压到一半左右,拿txt的《三国演义》,则只有70%了(不过rar也只能压到50%多)。全部文件解压出来和原文件二进制比较完全一致,证明了算法的正确性。
实践表明选用的最大位宽越大压缩率越高,但是内存开销和时间开销都最大,而且由于最大位宽比较大,进行位紧缩后效果不明显,而14位的最大位宽综合效果最理想。如果是在嵌入式环境下使用的话,使用小一点的最大位宽开销要小一些,不过不要小于12位。
有了这个Python版本,接下来准备用C语言再实现一遍的时候就有参考了。
# -*- coding: gbk -*-
def LZW ( inStr,narrow=False,bits=14):
'''使用LZW压缩算法压缩。
narrow为True时输出字节流位紧缩
默认最大位宽14位,允许范围12~16位'''
if isinstance(inStr,str):
inStr=list(inStr)
for i in range(len(inStr)):
inStr[i]=ord(inStr[i])
sOutStr=[256] #先放一个 开始&清除 标记
mTagMap={} #字典
iTagCurrent=258 #当前的标记总数 0~255 标记256 为begin & clear , 257 为end
iBitsLen=9
iTag=inStr[0] #当前标记
ii=0
cTemp=0
if bits>16 or bits<12:
return None
iMaxLen=(1<<bits)-1 #最多允许的标记数,由位宽决定
for ii in range(1,len(inStr)):
cChar=inStr[ii]
cTemp=(iTag<<8)+cChar #(前缀 后缀)
if cTemp in mTagMap: #该(前缀 后缀)已存在
iTag=mTagMap[cTemp] #取出其标记
else: #不存在
sOutStr.append(iTag) #将前缀放入输出流
mTagMap[cTemp]=iTagCurrent
iTagCurrent+=1 #增加一个标记,并放入字典
iTag=cChar #当前标记为后缀
if iTagCurrent>=iMaxLen: #如果到达了最大标记数,清除字典,从头开始
sOutStr.append(256)
mTagMap={}
iTagCurrent=258
if iTag!=0:
sOutStr.append(iTag)
sOutStr.append(257) #放入结束标记
if narrow: #位紧缩
return Narrow(sOutStr)
else:
return sOutStr
def Narrow (sOutStr):
sOutN=[]
iTemp=0
BitLeft=0
nowBit=9 #当前位宽
nowStag=1<<nowBit #当前位宽允许的标记数
nowTagCount=258
for cChar in sOutStr:
iTemp=iTemp+(cChar<<BitLeft)
nowTagCount+=1
BitLeft+=nowBit
if cChar==256:
nowBit=9
nowStag=1<<nowBit
nowTagCount=258
if nowTagCount>=nowStag:
nowBit+=1
nowStag=1<<nowBit
while BitLeft>=8:
sOutN.append(iTemp&0xff)
iTemp=iTemp>>8
BitLeft-=8
if BitLeft>0:
sOutN.append(iTemp)
return sOutN
def UnNarrow (inStr):
sOut=[]
iTemp=0
BitLeft=0
nowBit=9
nowStag=1<<nowBit
mask=nowStag-1
nowTagCount=258
for cChar in inStr:
iTemp=iTemp+(cChar<<BitLeft)
BitLeft+=8
if BitLeft>=nowBit:
cTemp=iTemp&mask
iTemp=iTemp>>nowBit
BitLeft-=nowBit
sOut.append(cTemp)
nowTagCount+=1
if nowTagCount>=nowStag:
nowBit+=1
nowStag=1<<nowBit
mask=nowStag-1
if cTemp==256:
nowBit=9
nowStag=1<<nowBit
mask=nowStag-1
nowTagCount=258
if BitLeft>0:
sOut.append(iTemp)
return sOut
def deTag ( mTagMap,nowTag,outStr):
'''将一个标记转化为元字符序列,并放入到输出流中'''
if nowTag>=0:
sTemp=[]
while nowTag>255:
pair=mTagMap[nowTag]
sTemp.append(pair[1])
nowTag=pair[0]
sTemp.append(nowTag)
sTemp.reverse()
outStr+=sTemp
return nowTag
def UnLZW ( inStr , narrow=False):
if narrow:
inStr=UnNarrow(inStr)
mTagMap={}
outStr=[]
nowTagCount=258
sTemp=[]
nowTag=-1
for cChar in inStr:
if cChar==256:
if nowTag>=0:
deTag(mTagMap,nowTag,outStr)
mTagMap={}
nowTagCount=258
nowTag=-1
elif cChar==257:
if nowTag>=0:
deTag(mTagMap,nowTag,outStr)
nowTag=-1
return outStr
elif nowTag==-1: #刚开始
nowTag=cChar
else:
pair=[nowTag,0]
mTagMap[nowTagCount]=pair
nowTagCount+=1
surfix=cChar
while surfix>255:
if surfix not in mTagMap:
print 'Thera are errors in input'
return outStr
surfix=mTagMap[surfix][0]
pair[1]=surfix
deTag(mTagMap,nowTag,outStr)
nowTag=cChar
def fileLZW ( inFile , outFile , bit=14 ):
import os
fsize=os.stat(inFile)[6]
f=os.open(inFile,os.O_BINARY)
s=os.read(f,fsize)
os.close(f)
a=LZW(s,True,bit)
f=open(outFile,'wb')
outs=''
for i in a:
outs+=chr(i)
f.write(outs)
f.close()
def fileUnlzw ( inFile , outFile):
import os
fsize=os.stat(inFile)[6]
f=os.open(inFile,os.O_BINARY)
s=os.read(f,fsize)
a=[]
for i in s:
a.append(ord(i))
b=UnLZW(a,True)
f=open(outFile,'wb')
outs=''
for i in b:
outs+=chr(i)
f.write(outs)
f.close()