我前面写了一篇《学习笔记:用python3实现全手工解压zip文件,包含所有实现的源代码》,基本上实现了zip文件的解压(单个压缩文件),但是我发现一个严重问题,就是当被解压文件比较大时,lz77解压变得奇慢无比!根本无法接受!
经过很长很长时间盯着屏幕的思考,我发现盯着屏幕没有用(哈哈),我开始反思我原来的lz77解压代码如下:
def unlz77(lzlist):
result = [] # 解码结果
winside = 0 # 滑动窗口右边界
slidewin = "" # 滑动窗口,注意我这里的滑动窗口一开始的宽度为“0”!!!
for item in lzlist:
if len(item)==1:
result+=[ord(item)]# 转换为数值
winside+=1 # 滑动窗口右边界加1
if winside>=swlen:
slidewin = result[winside-swlen:winside] # 滑动窗口向右滑动一个字符
else:
slidewin = result[0:winside] # 增加滑动窗口
elif item[0]=="(" and item[-1]==")" and item.count(",")==1: # 有重复字符串,但最后没有字符,所以是二元组
myitem=item[1:len(item)-1].split(",")
result+=searchstr(slidewin, int(myitem[0]), int(myitem[1])) # myitem[0]是重复字符串距离,myitem[1]是长度
winside+=int(myitem[1])
if winside>=swlen:
slidewin = result[winside-swlen:winside] # 滑动窗口向右滑动一个字符
else:
slidewin = result[0:winside] # 增加滑动窗口
elif item[0]=="(" and item[-1]==")" and item.count(",")==2: # 有重复字符串,且最后有字符,所以是三元组
myitem=item[1:len(item)-1].split(",")
result+=searchstr(slidewin, int(myitem[0]), int(myitem[1]))+[ord(myitem[2])] # myitem[2]是下一个字符
winside+=int(myitem[1])+1 # 滑动窗口右边界加上重复字符串长度再加上下一个字符的长度(1)
if winside>=swlen:
slidewin = result[winside-swlen:winside] # 滑动窗口向右滑动一个字符
else:
slidewin = result[0:winside] # 增加滑动窗口
else:
return("lz77压缩列表错误,无法解压!")
return(result)
经过调试,我发现问题不在searchstr函数(因为就算不通过此函数直接给出返回值依然慢!),最后我尝试给滑动窗口(slidewin)赋值为的一个固定的字符串,当然这样解压出来的结果肯定是错误的,但是我的目的是查找效率低的原因,不关心解压结果,这也是调试的一个方法!
滑动窗口(slidewin)的值不变之后,解压速度立即变得非常块,几乎可以忽略不记!好,找到问题了,那就改善呗,我尝试改进slidewin = result[winside-swlen:winside]这一句,因为如果被解压文件很大的话,这个result会越来越大,我觉得很可能这里会变慢,调试的时候,也发现解压数据流的时候,一开始解压很快,后来越来越慢,我改了一下,让slidewin在每次循环时自己自减前面的部分,自增后面的部分,不再从result中切片出来,但是改完后速度没有任何变化!!郁闷那!
我痛苦地调试了两天,网上也查了好多资料,但是python的这部分代码很少,没找到有价值的信息。
我甚至怀疑python的效率有问题,正当准备放弃的时候,我突发灵感,根据官方文档,lz77在压缩时是根据滑动窗口来寻找重复字符串的,这个是必须的,否则压缩结果会不同!但是解压时官方也用到了滑动窗口,这个从算法上看是顺利成章的,因为虽然不用滑动窗口不会影响解压结果,但是会大大的增加搜索范围!
但是我又想,我今天就试试不用滑动窗口到底能有多慢,反正已经很慢了!于是乎我把result+=searchstr(slidewin, int(myitem[0])直接换成了result+=searchstr(result, int(myitem[0]), 干脆不用滑动窗口了,我就在已解压完的结果中直接搜索重复值并解压字符串,结果令人难以置信,原来需要几十秒的解压时间现在居然秒解!我懵了!
看来我对python真的一点都不了解,我只能猜测:python是一个面向对象的语言,一切皆对象,那么你不能总是用c语言的思维去考虑问题!既然result是一个对象,那么直接调用可能远比对它切来切去,甚至还会重建对象效率高得多!
总之,这次学到的经验是:对象操作尽量不要总去对它进行增删改,更不要经常根据一个对象创建新的对象等等,尽量拿过来就读其中的数据!
最后的代码如下:
def unlz77(lzlist): # 解压lz77
result = [] # 解码结果
for item in lzlist:
if type(item)==int:
result+=[item] # 用append速度差不多
# 有重复字符串,但最后没有字符,所以是二元组
elif len(item)==2: ##下面用result[-1*maxdis:]也行,稍慢点,但省空间!
result+=searchstr(result, item[0], item[1]) # item[0]是重复字符串距离,item[1]是长度
elif len(item)==3:
result+=searchstr(result, item[0], item[1])+[item[2]] # item[0]是重复字符串距离,item[1]是长度
else:
print("压缩数据错误!")
return(result)
是不是清爽了很多,速度还提高了N倍,看来我们还是要学习学习再学习呀!
by lzq2000 泉中流 2021.4.13 8:31

997

被折叠的 条评论
为什么被折叠?



