在只接受有效UTF8的系统上存储任意二进制数据

5 投票
3 回答
549 浏览
提问于 2025-04-18 23:34

我有一些任意的二进制数据。我需要把它存储在一个要求使用有效UTF8编码的系统里。这个数据不会被当作文本来解释,我只是需要把它放进去,然后能够取出来并恢复我的二进制数据。

显然,使用base64编码是可以的,但我不想让数据变得那么大。

我该如何在Python 2.7中轻松实现这个呢?

3 个回答

0

如果你的应用真的需要用图形的方式来表示256种不同的字节值,其实你只需要256个Unicode编码点就可以解决问题。

ASCII码的33到127是很简单的,Unicode编码点的160到255也可以用来表示自己,但你可能想排除一些难以区分的,比如á、å、ä等,因为如果要让OCR(光学字符识别)或者人类来处理这些字符,可能会搞混。剩下的字符可以从可以用两个字节表示的编码点中选择,这个范围很大,但很多字符在大多数显示方式下也很难区分。

这个方案并没有尝试进行任何形式的压缩。如果压缩数据在你的应用中很重要,我想在编码之前先压缩数据会得到更好的效果。

0

你可以把你的字节数据当作8859-1格式来解码,这样总是能得到一个有效的Unicode字符串。然后你可以把它转成UTF8格式:

utf8_data = my_bytes.decode('iso8859-1').encode('utf8')

一般来说,你的数据中大约有一半的内容在0到127这个范围内,这部分在UTF8中只占用一个字节;而另一半的数据在128到255这个范围内,这部分在UTF8中会占用两个字节。所以,最终的结果会比你输入的数据大50%。

如果你的数据有任何结构,比如说是有规律的,那么像Martijn建议的那样用zlib进行压缩,可能会减小数据的大小。

4

你需要用ASCII字符来表示你的数据。使用Base64是一种最有效的方法(在Python标准库中可以找到),它能把二进制数据转化为可以打印的文本,并且是UTF-8安全的。虽然这样表示同样的数据会多占用33%的空间,但其他方法可能会占用更多的空间。

你可以结合压缩来减少占用的空间,但要让压缩成为可选项(给数据标记),只有在数据变小的时候才实际使用它。

import zlib
import base64

def pack_utf8_safe(data):
    is_compressed = False
    compressed = zlib.compress(data)
    if len(compressed) < (len(data) - 1):
        data = compressed
        is_compressed = True
    base64_encoded = base64.b64encode(data)
    if is_compressed:
        base64_encoded = '.' + base64_encoded
    return base64_encoded

def unpack_utf8_safe(base64_encoded):
    decompress = False
    if base64_encoded.startswith('.'):
        base64_encoded = base64_encoded[1:]
        decompress = True
    data = base64.b64decode(base64_encoded)
    if decompress:
        data = zlib.decompress(data)
    return data

这里的'.'字符不是Base64编码的一部分,所以我用它来标记压缩后的数据。

你还可以去掉Base64编码数据末尾的1或2个=填充字符;在解码时可以再加上(在末尾加上'=' * (-len(encoded) * 4)),不过我不太确定这样做是否值得。

你还可以通过切换到Base85编码来进一步节省空间,这是一种4对5的ASCII安全编码,二进制数据的额外开销只有20%。在Python 2.7中,这个编码只能通过外部库使用(Python 3.4 将其添加到base64库中)。在2.7中,你可以使用python-mom项目

from mom.codec import base85

并将所有的base64.b64encode()base64.b64decode()调用替换为base85.b85encode()base85.b85decode()调用。

如果你100%确定在数据传输过程中没有任何环节会把你的数据当作文本处理(可能会改变行分隔符,或者解释并改变其他控制代码),你也可以使用Base128编码,这样额外开销会减少到14.3%(每7个字节增加8个字符)。不过,我不能推荐一个可以通过pip安装的Python模块;有一个GitHub上的模块,但我没有测试过它。

撰写回答