What is the most efficient binary to text encoding?
我能找到的最接近的竞争者是Yenc(2%)和Ascii85(25%的开销)。关于Yenc,似乎存在一些问题,主要是因为它使用了一个8位字符集。这又引出了另一个想法:是否存在基于UTF-8字符集的二进制到文本编码?
这实际上取决于二进制数据的性质以及"文本"对输出的约束。
首先,如果二进制数据没有被压缩,请尝试在编码之前进行压缩。然后我们可以假设1/0或单个字节的分布或多或少是随机的。
现在:你为什么需要文本?通常情况下,这是因为通信通道并不能平等地通过所有字符。例如,您可能需要纯ASCII文本,其可打印字符范围为0x20-0x7e。您可以使用95个字符。每个字符理论上可以对log2(95)~=6.57位/字符进行编码。很容易定义一个非常接近的转换。
但是:如果你需要一个分隔符呢?现在您只有94个字符等,因此编码的选择实际上取决于您的需求。
举一个非常愚蠢的例子:如果您的通道无问题地通过了所有256个字符,并且您不需要任何分隔符,那么您可以编写一个简单的转换来实现100%的效率。:-)如何这样做留给读者作为练习。
对于任意编码的二进制数据,UTF-8不是一种好的传输方式。它能够传输值0x01-0x7F,开销仅为14%。我不确定0x00是否合法;可能不合法。但是,超过0x80的任何内容都会以UTF-8扩展到多个字节。我会将UTF-8视为一个通过0x01-0x7F或126个唯一字符的受限通道。如果您不需要除颤器,那么您可以每个字符传输6.98位。
这个问题的一般解决方案是:假设一个由n个字符组成的字母表,其二进制编码为0到n-1。(如果编码不是假设的,那么使用查找表在中间0..n-1表示和实际发送和接收的内容之间进行转换。)
假设字母表中有95个字符。现在:这些符号中的一些代表6位,有些代表7位。如果我们有一个6位符号和B 7位符号,那么:
A+B=95(符号总数)2a+b=128(可设置的7位前缀总数)。您可以以6位符号或7位符号开头2个前缀。)
解出系统,你得到:A=33,B=62。现在可以创建符号表:<前>原始编码000000 000000000000 1 000000 000000…100000 01000001000010 01000011000011 0100010…111111 0 01111111 1011110[/cc]
要进行编码,首先从输入的6位移位。如果这六位大于或等于100001,则移动另一位。然后查找相应的7位输出代码,翻译到适合的输出空间并发送。您将在每次迭代中移动6或7位输入。
要解码,接受一个字节并转换为原始输出代码。如果原始代码小于0100001,则将相应的6位移到输出上。否则,将相应的7位移到输出上。每次迭代将生成6-7位输出。
对于均匀分布的数据,我认为这是最佳的。如果您知道您的源代码中的0比1多,那么您可能需要将7位代码映射到空格的开头,以便更可能使用7位代码。
简短的回答是:不,仍然没有。
我遇到了将尽可能多的信息编码成JSON字符串的问题,这意味着没有控制字符、反斜杠和引号的UTF-8。
我出去研究了在有效的UTF-8字节中可以压缩多少位。我不同意这样的回答:UTF-8带来了太多的开销。这不是真的。
如果只考虑一个字节序列,它就和标准的ASCII一样强大。表示每字节7位。但是如果你删掉所有的特殊字符,你就会得到类似ascii85的东西。
但是在更高的平面上控制字符更少。所以,如果你使用6字节的数据块,你将能够对每个数据块编码5字节。在输出中,您将得到任意长度的utf-8字符的任意组合(1到6个字节)。
这将给你一个更好的结果比ASCII85:5/6而不是4/5,83%的效率而不是80%。理论上,如果块长度越长,效果越好:大约84%的数据是19字节的。
在我看来,编码过程变得过于复杂,但利润微乎其微。所以ascii85或者它的一些修改版本(我现在看z85)会更好。
去年我搜索了最有效的二进制到文本编码。我自己意识到,紧凑不是唯一的标准。最重要的是在哪里可以使用编码字符串。例如,
我的选择是
最后,我用C/C++编写了Z85库,并将其应用到生产中。
根据维基百科
basE91 produces the shortest plain ASCII output for compressed 8-bit binary input.
如果您只使用ASCII字符,不想使用不可打印的字符,那么目前base91是最好的编码方式。它还具有闪电般快速的编码/解码速度,因为可以使用查找表,而不像base85,它必须使用慢速分割进行解码。
高于这个基数122将有助于提高效率一点,但它不是8位的干净。但是,因为它是基于UTF-8编码的,所以可以用于许多目的。现在8位的clean就没有意义了
Base-122 Encoding
Base-122 encoding takes chunks of seven bits of input data at a time. If the chunk maps to a legal character, it is encoded with the single byte UTF-8 character:
0xxxxxxx . If the chunk would map to an illegal character, we instead use the the two-byte UTF-8 character:110xxxxx 10xxxxxx . Since there are only six illegal code points, we can distinguish them with only three bits. Denoting these bits assss gives us the format:110sssxx 10xxxxxx . The remaining eight bits could seemingly encode more input data. Unfortunately, two-byte UTF-8 characters representing code points less than 0x80 are invalid. Browsers will parse invalid UTF-8 characters into error characters. A simple way of enforcing code points greater than 0x80 is to use the format110sss1x 10xxxxxx , equivalent to a bitwise OR with 0x80 (this can likely be improved, see §4). Figure 3 summarizes the complete base-122 encoding.
http://blog.kevinalbs.com/base122
除了维基百科上列出的,还有博玛新闻:
B-News (or bommanews) was developed to lift the weight of the overhead inherent to UUEncode and Base64 encoding: it uses a new encoding method to stuff binary data in text messages. This method eats more CPU resources, but it manages to lower the loss from approximately 40% for UUEncode to 3.5% (the decimal point between those digits is not dirt on your monitor), while still avoiding the use of ANSI control codes in the message body.
可与Yenc进行比较:来源
yEnc is less CPU-intensive than B-News and reaches about the same low level of overhead, but it doesn't avoid the use of all control codes, it just leaves out those that were (experimentally) observed to have undesired effects on some servers, which means that it's somewhat less RFC compliant than B-News.
- http://b-news.sourceforge.net网站/
- http://www.iguana.be/~stef/
- http://bnews-plus.sourceforge.net/
听起来你已经有答案了,马克。UTF-8作为二进制编码是不有用的,因为任何大于一个字节的UTF-8字符即使存储文本(每字节2位或更多)也会有超过25%的开销。base64编码已经比这更好了。
我最近需要将二进制编码为ASCII,这就是我想到的。我不知道这是否是最有效的(可能不是),但它是简单和快速的。基本上,我将一个字节编码为十六进制,但不使用我使用的基集(0-9,a-f)(a-p)。因为集合是连续的,所以不需要任何表查找。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //buff is a unsigned character array containing the binary data //N is the number of bytes to be encoded string simple_encode(unsigned char *buff, int N) { string sEncode =""; for(int i = 0; i<N; i++) { sEncode += (97 + (buff[i] >> 4)); sEncode += (97 + (buff[i] & 0x0F)); } return sEncode; } //sbuff is a string containing the encoded ascii data //szDecoded is an unsigned char array that has been allocated to 1/2 //the length of sbuff //N is an integer pointer and returns the number of converted bytes void simple_decode(string sbuff, unsigned char *szDecode, int *N) { *N = sbuff.length()/2; for(int i=0; i < *N; i++) { szDecode[i] = ((sbuff.at(2*i)-97) << 4) + (sbuff.at(2*i+1)-97); } } |