我有数据(主要是一系列 numpy 数组),我想将其转换为可以复制/粘贴/通过电子邮件发送等的文本。我创建了以下公式来执行此操作。
def convert_to_ascii85(x):
p = pickle.dumps(x)
p = zlib.compress(p)
return b64.b85encode(p)
Run Code Online (Sandbox Code Playgroud)
我的问题是它产生的字符串比它需要的要长,因为它只使用字母、数字和符号的子集。如果我能够使用 unicode 进行编码,我觉得它可以产生更短的字符串,因为它可以访问更多字符。有没有办法做到这一点?
编辑澄清:我的目标不是最小数量的 data/information/bytes。我的目标是最少的字符数。原因是我发送数据的通道以字符(准确地说是 100k)而不是字节(奇怪,我知道)为上限。我已经测试过我可以发送 100k unicode 字符,我只是不知道如何将我的字节转换为 unicode。
更新:我刚刚看到您更改了问题以澄清您关心字符长度而不是字节长度。这是一个非常奇怪的约束。我以前从未听说过。我不知道该怎么办。但是,如果这是您的需要,并且您想要可预测的阻塞行为,那么我认为您的问题非常简单。只需选择可以表示最可能的唯一字符的兼容字符编码,然后在该字符集中映射二进制文件的块,这样每个块都是最长的,但包含的位数少于您的二进制文件中可表示的字符数字符编码。每个这样的块然后变成单个字符。由于这个约束有点奇怪,我不知道是否有库可以做到这一点。
UPDATE2:我对上述内容感到好奇,我只是谷歌搜索并发现了这个:https ://qntm.org/unicodings 。如果您的工具和通信渠道可以处理 UFT-16 或 UTF-32,那么您可能会寻求使用它。如果是这样,我希望这篇文章能够为您找到您正在寻找的解决方案。我认为这篇文章仍在针对字节长度与字符长度进行优化,所以也许这不会提供最佳解决方案,但它只能提供帮助(每个字符 32 个潜在位,而不是 7 或 8 个)。我找不到任何寻求单独优化字符数的东西,但也许像 Base65536 这样的 UTF-32 方案是您的答案。查看https://github.com/qntm/base65536。
如果您关心的是字节长度,并且您想坚持使用“可打印字符”或“纯可打印文本”通常的含义,那么这是我的原始答案......
有一些选项可以从 Base85 以外的编码中获得更好的“可读文本”编码空间效率。还有一个案例可以放弃更多的空间效率并使用 Base64。在这里,我将说明同时使用 Base85 和 Base64 的情况。如果您可以使用 Base85,那么您的二进制文件的膨胀只会减少 25%,并且这样做可以省去很多麻烦。
如果您试图将任意二进制编码为“纯文本”,那么 Base85 非常接近您要做的最好的方法,如果您想要一种可以在逻辑上分解为有意义的“纯文本”编码,则它是您可以做的最好的,可预测的块。理论上,您可以使用在高位 ASCII 范围内使用可打印字符的字符集,但经验表明,如果许多工具和通信渠道无法处理直接二进制,则无法很好地处理高位 ASCII。尝试使用每 4 个二进制字节的额外 5 位或因此可以通过使用 256 位高位 ASCII 与 128 位 ASCII 来潜在地使用,您不会获得太多额外的空间节省。
对于任何 BaseXX 编码,该算法获取传入的二进制位并使用它可以使用的 XX 可打印字符尽可能紧密地对它们进行编码。Base85 将比 Base64 更紧凑,因为它使用的可打印字符 (85) 比 Base64 (64 个字符) 多。
标准 ASCII 中有 95 个可打印字符。所以有一个 Base95 是使用所有可打印字符的最紧凑的编码。但是尝试使用所有 95 位是混乱的,因为它会导致传入位的不均匀阻塞。每 4 个二进制字节映射到一些小于 5 的小数字符。
事实证明,将 4 个字节编码为 5 个可打印字符需要 85 个字符。许多人会选择增加大约 10% 的额外长度,以实现每 4 个编码字节正好产生 5 个 ASCII 字符的事实。这只是二进制大小的 25% 膨胀。对于它节省的所有头痛来说,这一点也不坏。因此,Base85 背后的动机。
Base64 用于生成更长但问题更少的编码。不使用对各种文本文档(如 HTML、XML、JSON 等)造成麻烦的字符。通过这种方式,Base64 几乎可以在任何上下文中使用而无需任何转义。您必须对 Base85 更加小心,因为它不会丢弃任何这些有问题的字符。为了编码/解码效率,它使用 33 (“!”) 到 117 ('u') 的范围,从 33 而不是 32 开始,只是为了避免经常出现问题的空格字符。它不使用的 'u' 上方的字符没什么特别的。
所以这就是二进制-> ASCII 编码方面的故事。另一个问题是在将二进制表示编码为 ASCII 的阶段之前,您可以做些什么来减小所表示内容的大小。您选择使用pickle.dumps()和zlib.compress()。如果这些是您的最佳选择,则留待另一次讨论...