Python实现英文文本的LZW编码压缩

LZW编码采用隐字典码的形式完成压缩。
文本中符号并不是独立存在的,前一个符号对后一个符号的出现有着很大影响。
LZW算法利用文本开头部分的特征,作为整个文本的特征,即利用文本开头部分的内容,生成固定规模的字典,即符号–码符号对,然后利用于整个文本,将文本符号转化为码符号。
LZW首先要有初始字典(可以为空),再通过文本符号添加字典中的元素。每次读取文本中一个符号,若该符号已出现在字典中,则将改符号作为前缀,继续读取下一位。
例如
已有字典{‘A’:0,‘B’:1,‘C’:2},字符串’AAABABC’,

  1. 第一步读取’A’,‘A’已存在于字典中,所以’A’作为前缀,读取字符串后一位’A’,此时当前符号为’AA’,没在字典中,于是字典添加符号–码符号对’AA’:3,字典更新为{‘A’:0,‘B’:1,‘C’:2,‘AA’:3}。
  2. 清空前缀,读取字符串第三位’A’,‘A’在字典中,于是’A’作为前缀,读取下一位’B’,当前符号为’AB’,没在字典中,于是添加符号–码符号对’AB’:4,字典更新为{‘A’:0,‘B’:1,‘C’:2,‘AA’:3,‘AB’:4}
  3. 清空前缀,读取字符串第五位’A’,‘A’位于字典中,于是’A’作为前缀,读取下一位’B’,当前符号为’AB’,‘AB’在字典中,于是’AB’作为前缀,读取下一位’C’,当前符号为’ABC’,没在字典中,于是字典更新为{‘A’:0,‘B’:1,‘C’:2,‘AA’:3,‘AB’:4,‘ABC’:5}。

{‘A’:0,‘B’:1,‘C’:2,‘AA’:3,‘AB’:4,‘ABC’:5}即为{‘A’:0,‘B’:1,‘C’:2}根据字符串’AAABABC’生成的字典。
LZW常用ASCII码作为初始字典,生成字典的值从255开始。实现时,更新字典的同时需要记录下文本符号对应的码符号。
具体步骤为

  1. 生成初始字典
keys=[]
values=[]
for i in range(255):
    keys.append(chr(i))
    values.append(i)
  1. 更新字典
Path='C:\Users\88466\Desktop\Gone with the wind.txt'# 文本路径
with open(Path,'r') as f1:
	data_string=fi.read()
digit=12 #生成字典的大小,12即表示字典有2的12次方个元素,常取12,16
Compress_list=[] #存放文本符号对应的码符号
pW_s=''          #前缀字符串 
temp_index=len(keys)
max_time=len(data_string)
i=0
while(i<max_time):
    cW_s=data_string[i] #当前字符串
    #该结构用于判断是否data_string是否全部读取完毕
    try:
        sW_s=data_string[i+1]
    except:
        Compress_list.append(values[keys.index(cW_s)])
        break
    temp_index=temp_index+1
    if cW_s+sW_s in keys:
        while(cW_s+sW_s in keys):
            i=i+1
            cW_s=cW_s+sW_s
            try:
                sW_s=data_string[i+1]
            except:
                    Compress_list.append(values[keys.index(cW_s)])
                    break
    keys.append(cW_s+sW_s)
    values.append(temp_index-1)
    try:
        Compress_list.append(values[keys.index(cW_s)])
    except:
        print('未记录:',cW_s)
    i=i+1
    if len(keys)>=2**digit:
        break
    #keys与values即为更新的字典
  1. 利用字典将文本符号转为码符号
while(i<max_time):
    b=0
    cW_s=data_string[i]
    try:
        sW_s=data_string[i+1]
    except:
        Compress_list.append(values[keys.index(cW_s)])
        break
    while(cW_s+sW_s in keys):
        i=i+1
        cW_s=cW_s+sW_s
        try:
            sW_s=data_string[i+1]
        except:
            Compress_list.append(values[keys.index(cW_s)])
            break
    try:
        Compress_list.append(values[keys.index(cW_s)])
    except:
        print('未记录:',cW_s)
    i=i+1
    #Compress_list为0~2^digit的数字,即为文本符号对应的码符号。
  1. 压缩
#将列表转为二进制字符串
def list2bit_string(li):
    bitstring=''
    for i in range(len(li)):
        temp_string=bin(li[i]).split('b')[1]
        while(len(temp_string)<digit):
            temp_string='0'+temp_string
        bitstring=bitstring+temp_string
    return bitstring
Compress_bit_string=list2bit_string(Compress_list)
print('字符串生成完成,字符串总长度为',len(Compress_bit_string))
#将二进制字符串转为字节流
def bit_string2bytes(bs):
    bt=bytearray()
    time=round(len(bs)/8)
    for i in range(time):
        bs_k=int(bs[i*8:(i+1)*8],2)
        bt.append(bs_k)
    bt.append(int(bs[-4:],2))
    return bytes(bt)
Compress_bytes=bit_string2bytes(Compress_bit_string)
print('字节流生成完成')
with open('Compress_Gone with the wind','wb') as f2:
    f2.write(Compress_bytes)
print('存储完成')
  1. 解压缩,由于采用的是隐字典,即字典隐藏在码符号中,解压缩时要将字典还原回去。

(1)获取码符号


with open('Compress_Gone with the wind','rb') as f3:
    Compress_bytes=f3.read()
            
    #将二进制字节流转为二进制字符串流
def bytes2bitstring(byte):
    bs=''
    for i in byte:
        bs_k=str(bin(i)).split('b')[1]
        while len(bs_k)<8:
            bs_k='0'+bs_k
        bs=bs+bs_k
    bs=bs
    return bs
Compress_bit_string=bytes2bitstring(Compress_bytes)
print('字符串转换完成,字符串总长度为',len(Compress_bit_string))
#将字符串流转为列表
def bitstring2list(string):
    li=[]
    time=round(len(string)/digit)-1
    for i in range(time):
        temp_s=string[digit*i:digit*(i+1)]
        li.append(int(temp_s,2))
    return li
Compress_list=bitstring2list(Compress_bit_string)
print('列表转换完成,列表总长度为',len(Compress_list))

(2)生成解压缩字典
例如已有字典{‘A’:0,‘B’:1,‘C’:2},码符号[0,1,1,3,6,2]

  1. 读取码符号0,0在字典中有对应’A’,即表明生成字典时,第一个位置的符号在字典中,但其作为前缀,连接下一位符号就已经不在字典内了,下一个码符号为1,对应’B’,所以此次添加元素为’AB’:3,字典更新为{‘A’:0,‘B’:1,‘C’:2,‘AB’:3}。
  2. 读取码符号1,1在字典中有对应’B’,下一个码符号为1,对应’B’,字典更新为{‘A’:0,‘B’:1,‘C’:2,‘AB’:3,‘BB’:4}
  3. 读取码符号1,1在字典中有对应’B’,下一个码符号为3,对应’AB’,表明在生成该字典元素,读取到了码符号1对应的’B’及码符号3对应的首符号’A’时添加了元素,即此次添加元素为’BA’:5,字典更新为{‘A’:0,‘B’:1,‘C’:2,‘AB’:3,‘BB’:4,‘BA’:5}
  4. 读取码符号3,3在字典中有对应’AB’,字典此次添加的符号定以’AB’开头,下一个码符号为6,其首符号为’A’,所以此次添加元素’ABA’:6,字典更新为{‘A’:0,‘B’:1,‘C’:2,‘AB’:3,‘BB’:4,‘BA’:5,‘ABA’:6}
  5. 读取码符号2,2在字典中对应’C’,且已为最后一个码符号,字典不再更新。

由上述步骤可见,每读取一个码符号,就要再字典中添加一次元素

LZW (Lempel-Ziv-Welch) 压缩算法是一种无损数据压缩技术,它通过构建并编码频繁出现的词典来减少数据量。Python实现LZW算法通常涉及以下几个步骤: 1. **初始化**: - 创建一个空的字典,用于存储已处理过的字符串。 - 将输入数据的第一个字符加入字典,并设置初始的两个元素。 2. **循环处理**: - 读取下一个字符,如果这个字符和字典中当前最短字符串的组合不在字典中,那么将这个组合添加到字典并生成一个新值。 - 否则,使用字典中的新字符串和下一个字符继续组合。 3. **编码过程**: - 遇到新的字符串时,用它在字典中的值来替换原始序列,同时更新字典。 4. **生成压缩流**: - 输出字典中的每个键值对,其中键是原来的字符串,值是其对应的压缩编码。 5. **解码过程**: - 反向操作,接收压缩流,每次遇到的编码值对应字典中的字符串,将它们连接起来直到达到一个新的未编码组合。 以下是一个简单的Python LZW解码示例: ```python def lzw_decompress(code): dictionary = {str(i): chr(i) for i in range(256)} dict_size = 256 string = dictionary['0'] output = '' while True: if len(code) > 0: next_code = code[:dict_size] code = code[dict_size:] if next_code in dictionary: output += dictionary[next_code] else: output += string + dictionary[next_code] dictionary[string + next_code] = len(dictionary) string = '' if not code: break if len(string) == dict_size - 1: dictionary[string] = len(dictionary) string = '' return output # 示例用法 compressed_data = b'\x85\x01\x00\x86\x01\x00\x87\x01' decompressed_text = lzw_decompress(compressed_data) print(decompressed_text) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值