什么是最高效的二进制到文本编码?

39

目前我能找到的最接近的是yEnc(2%)和ASCII85(25%的开销)。yEnc存在一些问题,主要是因为它使用8位字符集。这也引发了另一个想法:是否有基于UTF-8字符集的二进制到文本编码?


5
注意,yEnc 并不会将二进制转换为文本,它将二进制转换为符合新闻协议(NNTP)的内容,这并不一定满足任何字符集要求,更不用说全部是可打印文本了。 - Maarten Bodewes
7个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
22

这实际上取决于二进制数据的性质以及“文本”对输出的约束条件。

首先,如果您的二进制数据未经压缩,请在编码之前进行压缩。然后,我们可以假设1/0或单个字节的分布基本上是随机的。

现在:为什么需要文本?通常,这是因为通信渠道不能平等通过所有字符。例如,您可能需要纯ASCII文本,其可打印字符范围从0x20-0x7E。您有95个字符可供使用。每个字符理论上可以编码log2(95) ~= 6.57位/字符。很容易定义一个转换,效果相当不错。

但是:如果您需要分隔符呢?现在您只有94个字符等等。因此,编码选择真的取决于您的要求。

举个非常愚蠢的例子:如果您的通道可以毫无问题地传递所有256个字符,并且您不需要任何分隔符,则可以编写一个简单的转换,实现100%的效率。:-) 如何实现留给读者作为练习。

UTF-8不适合任意编码的二进制数据传输。它可以使用14%的开销传输值0x01-0x7F。我不确定0x00是否合法;可能不是。但是,UTF-8中大于0x80的任何内容都会扩展为多个字节。我将UTF-8视为传递0x01-0x7F或126个唯一字符的受限频道。如果您不需要分隔符,则可以每个字符传输6.98位。

此问题的通用解决方案:假设N个字符的字母表的二进制编码为0到N-1。(如果编码不是所假定的,则使用查找表在我们的中间表示0..N-1和实际发送和接收的内容之间进行转换。)

假设字母表中有95个字符。现在:其中一些符号将表示6位,而一些符号将表示7位。如果我们有A个6位符号和B个7位符号,则:

A+B=95(符号总数)

2A+B=128(可以制作的7位前缀总数。您可以使用6位符号启动2个前缀,或者使用7位符号启动1个前缀。) 解决这个问题,您会得到:A=33,B=62。现在建立一个符号表:
原始      编码
000000  0000000
000001  0000001
...
100000  0100000
1000010 0100001
1000011 0100010
...
1111110 1011101
1111111 1011110
编码时,首先移开输入的6位。如果这6位大于或等于100001,则再移动另外1位。然后查找对应的7位输出代码,将其转换以适合输出空间并发送。每次迭代将移动输入的6或7位。 解码时,接受一个字节并将其转换为原始输出代码。如果原始代码小于0100001,则将相应的6位移动到输出中。否则将相应的7位移动到输出中。每次迭代将生成6-7位输出。 对于均匀分布的数据,我认为这是最优的。如果您知道源中有更多的零比一个,则可能希望将7位代码映射到空间的开头,以便更有可能使用7位代码。

你所描述的将N个等概率字符编码为B位或B-1位的通解,也被称为“相位编码”、“经济编码”或“截断二进制编码”。 - David Cary

11

简短的回答是:没有,仍然没有。

我遇到了将大量信息编码为JSON字符串的问题,即UTF-8无控制字符、反斜杠和引号。

我调研了一下你可以塞入有效UTF-8字节的比特数。我不同意那些声称UTF-8带来了太多开销的答案。这不是真的。

如果你只考虑一字节序列,那么它就像标准ASCII一样强大。也就是说,每字节7位。但是如果你去掉所有的特殊字符,你会得到像Ascii85一样的东西。

但是高位平面中的控制字符要少得多。因此,如果你使用6个字节组,你将能够在每个组中编码5个字节。在输出中,你将得到任意长度(从1到6个字节)的UTF-8字符的任意组合。

这将比Ascii85给出更好的结果:5/6而不是4/5,83%的效率而不是80%。理论上,在更高的块长度下,它将变得更好:在19字节块时约为84%。

在我看来,编码过程变得太复杂了,而提供的利润非常小。因此,Ascii85或一些修改版的Ascii85(我现在正在看Z85)会更好。


11

去年我寻找了最高效的二进制到文本编码方式。我认识到紧凑性不是唯一的标准,使用编码字符串的能力才是最重要的因素。例如,yEnc 的额外开销只有2%,但它是8位编码,所以使用范围非常有限。

我的选择是 Z85。它有25%的合理开销,并且编码字符串几乎可以在任何地方使用:XML、JSON、源代码等。详情请参见 Z85 规范

最后,我用C/C++编写了Z85 并将其用于生产中。


8
根据维基百科的介绍:

basE91可为压缩的8位二进制输入生成最短的纯ASCII输出。


1
basE91比base64和Z85更高效。但在HTML中显示其输出时要小心。它使用像(<,>,&)这样的字符,应该进行转义(Z85也有此问题)。 - bryc
1
我们能否用UTF-8做得更好? - nog642
1
是的 @nog642 - Base122 编码是一种空间高效的 UTF-8 二进制文本编码,比等效的 base-64 编码数据小约14%:http://blog.kevinalbs.com/base122#text_encodings_and_utf8 - Dan Froberg

6

如果你只能使用ASCII字符并且不想使用不可打印字符的话,目前最好的编码方式是Base91。它还具有极快的编码/解码速度,因为可以使用查找表进行编码,而不像Base85需要使用缓慢的除法进行解码。

如果需要更高效率的编码,可以尝试Base122,但它不符合8位清洁规范。然而,由于它基于UTF-8编码,它对许多用途来说应该是可行的。而现在的8位清洁规范已经没有实际意义了。

需要注意的是,Base122实际上是基于Base-128的,因为6个无效值(128-122)被特别编码,以便一系列14位可以用最多2个字节来表示,就像Base-128一样,其中7位将编码为1个字节,并且实际上可以优化为比Base-128更有效率的方案。

Base-122编码

Base-122编码每次处理7位输入数据。如果该块映射到合法字符,则使用单字节UTF-8字符进行编码:0xxxxxxx。如果该块将映射到非法字符,则使用双字节UTF-8字符:110xxxxx 10xxxxxx。由于只有六个非法代码点,我们只需用三位来区分它们。将这些位表示为sss给出了格式:110sssxx 10xxxxxx。剩余的八位似乎可以编码更多的输入数据。不幸的是,表示小于0x80的代码点的双字节UTF-8字符是无效的。浏览器将解析无效的UTF-8字符为错误字符。强制使用大于0x80的代码点的一种简单方法是使用格式110sss1x 10xxxxxx,相当于按位或运算符与0x80(这可能可以改进,请参见§4)。图3总结了完整的Base-122编码。

Base-122 encoding scheme

http://blog.kevinalbs.com/base122

还可参考像JavaScript字符串这样的场景是否适合使用Base128编码?


1
只是一个注意事项:似乎它可以生成用户无法复制的“控制字符”(例如复制粘贴)。 - Inkeliz

1

除了维基百科上列出的编码方式,还有Bommanews:

B-News(或bommanews)是为了减轻UUEncode和Base64编码固有的开销而开发的:它使用一种新的编码方法将二进制数据嵌入文本消息中。这种方法消耗更多的CPU资源,但它成功地将损失从UUEncode的约40%降低到3.5%(这些数字之间的小数点不是您显示器上的污垢),同时仍避免在消息正文中使用ANSI控制代码。

它类似于yEnc:来源

yEnc比B-News的CPU占用要少,并且达到了大致相同的低开销水平,但它并没有避免使用所有控制代码,只是省略了那些在某些服务器上观察到具有不良影响的代码,这意味着它比B-News稍微不符合RFC标准。


1
Bommanews的常见问题解答没有涉及支持哪些字符编码。我认为大多数8位代码页都是支持的,尽管7F可能存在,并且在IBM OEM字符集中是一个控制码。即使在Windows代码页中,818D8F909D也是控制字符。当打印这些内容时要小心,因为数据将会丢失。 - Maarten Bodewes
@Maarten:B-News使用字符0x20-0xFF。每个字符都是一个基于224的数字的单个数字,偏移量为0x20。每行“文本”都是一个巨大的数字,在解码和编码过程中转换为二进制。 Yenc几乎使用了0x00到0xFF的全部范围,二进制输入中的每个字节都简单地复制到文本输出中,仅转义0x00、0x0A和0x0D(以及转义字符本身,我不记得确切是什么)。 - Luc VdV
最后我重新审视了这个问题并投了反对票。yEnc和B-news用于处理新闻协议(如果我没记错的话是NNTP),这些编码并没有专门针对字符集,如UTF-8、ASCII或Windows-1252。因此,请注意这个错误在问题中也有所体现,所以我可能有点不公平。 - Maarten Bodewes
b-news和yEnc在Web浏览器中用于显示目的时效果不佳。Base64和Base91可以轻松复制粘贴,而b-news/yenc则不能。 - bryc

0

如果您正在寻找一种适用于大型字母表的高效编码方式,您可能想尝试escapeless。无转义252和yEnc都有1.6%的开销,但是对于前者,它是固定的并且事先已知,而对于后者,它实际上取决于字节的分布,范围从0到100%。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,