我有一个长字符串,我想将其压缩为新字符串,但输出字母仅包含[a-z]
[A-Z]
和{{1}的限制} characters。
我该怎么做,特别是在Python中?
答案 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)
为了进行比较,这里是每个算法扩展二进制数据的程度:
像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%。