字符串压缩:输出字母受限于字母数字字符

时间:2013-12-10 23:15:37

标签: python string compression

我有一个长字符串,我想将其压缩为新字符串,但输出字母仅包含[a-z] [A-Z]和{{1}的限制} characters。

我该怎么做,特别是在Python中?

2 个答案:

答案 0 :(得分:4)

虽然许多编码算法可以采用任意输出范围,但大多数实现都不能,并且如果输出范围不是2/16/256的幂,则许多算法的效率要低得多。

因此,您希望将其拆分为两部分:首先将一个字节流压缩为另一个字节流。然后将输出字节流编码为字母数字字符。 (如果你从不是字节流的东西开始,比如Python 3字符串或Python 2 unicode,那么将它编码为字节流是一个很简单的步骤。)

例如,如果你想要base64,你可以这样做:

import base64, zlib
compressed_bytes = zlib.compress(plain_bytes)
compressed_text = base64.b64encode(compressed_bytes)

不幸的是,你不需要base-64,因为它包含一些非字母数字字符。

您可以使用base32,它只包含大写字母和6位数字,代码的唯一更改是b32encode而不是encode。但这有点浪费,因为它只使用每8位中的5位,理论上你可以使用每个8位的~5.594。

如果你想以最佳方式做到这一点,并且你不能仅仅弯曲字母数字字符的要求,那么base62非常复杂,因为你不能逐字节地做,但一次只能用7936字节的块。这不会有趣或有效。通过分块(例如,一次32个字节)并浪费剩余的比特,可以合理地接近最优。但是你可能最好使用base64加上一个转义机制来处理不适合你的方案的两个字符。例如:

def b62encode(plain):
    b64 = base64.b64encode(plain)
    return b64.replace('0', '00').replace('+', '01').replace('/', '02')

def b62decode(data):
     b64 = '0'.join(part.replace('01', '+').replace('02', '/') 
                    for part in data.split('00'))
     return base64.b64decode(b64)

为了进行比较,这里是每个算法扩展二进制数据的程度:

  • base32:60.0%
  • 假基地62:39.2%
  • 现实基础62:~38%
  • 最佳基数62:34.4%
  • base64:33%

像base64这样的部分字节传输编码的重点在于它们非常简单且运行速度快。虽然你可以将它扩展到像base62这样的部分位编码,但是你失去了所有优点......所以如果伪base62不够好,我建议使用完全不同的东西。


要反转此操作,请按相反顺序反转所有相同步骤。

将所有内容放在一起,使用伪base62,并使用unicode / Python 3字符串:

plain_bytes = plain_text.encode('utf-8')
compressed_bytes = zlib.compress(plain_bytes)
b62_bytes = b62encode(compressed_bytes)
b62_text = b62_bytes.decode('ascii')

b62_bytes = b62_text.encode('ascii')
compressed_bytes = b62decode(b62_bytes)
plain_bytes = zlib.decompress(compressed_bytes)
plain_text = plain_bytes.decode('utf-8')

这就像它可以获得的那样复杂。

答案 1 :(得分:0)

有一种比基本62更简单的编码方案或基本64的修改,用于将输出限制为62个值。将输入作为比特流(事实上它是),然后将5或6位编码为每个输出字符。如果五位是00000或00001,则将其编码为62的前两个字符。否则,再取一个位,给出60个可能的值。使用剩下的60个字符。继续剩余的位。在末尾填充零位以获得最后的五位或六位。

解码更简单。您只需为接收到的每个字符发出五位或六位。你扔掉了那些不构成一个完整字节的额外位。

该方案产生的扩展为35%,接近理论最优值34.36%。