到目前为止,我能找到的最接近的竞争者是 yEnc (2%) 和 ASCII85 (25% 开销)。yEnc 似乎存在一些问题,主要围绕它使用 8 位字符集这一事实。这导致了另一个想法:是否存在基于 UTF-8 字符集的文本编码二进制文件?
9 回答
这实际上取决于二进制数据的性质,以及“文本”对输出的约束。
首先,如果您的二进制数据未压缩,请尝试在编码之前进行压缩。然后我们可以假设 1/0 或单个字节的分布或多或少是随机的。
现在:为什么需要文本?通常,这是因为通信通道不会平等地通过所有字符。例如,您可能需要纯 ASCII 文本,其可打印字符范围为 0x20-0x7E。你有 95 个角色可以玩。每个字符理论上可以编码 log2(95) ~= 6.57 bits per character。定义一个非常接近的变换很容易。
但是:如果你需要一个分隔符怎么办?现在你只有 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 位。如果我们有 A 6 位符号和 B 7 位符号,那么:
A+B=95(符号总数) 2A+B=128(可以制作的 7 位前缀的总数。您可以以 2 个以 6 位符号开头的前缀,或以 1 个以 7 位符号开头。 )
求解系统,你得到:A=33,B=62。您现在构建一个符号表:
原始编码 000000 0000000 000001 0000001 ... 100000 0100000 1000010 0100001 1000011 0100010 ... 1111110 1011101 1111111 1011110
要编码,首先移出 6 位输入。如果这六位大于或等于 100001,则移动另一位。然后查找相应的 7 位输出代码,翻译成适合输出空间并发送。每次迭代您将移动 6 或 7 位输入。
要解码,接受一个字节并转换为原始输出代码。如果原始代码小于 0100001,则将相应的 6 位移到您的输出上。否则将相应的 7 位移到您的输出上。每次迭代您将生成 6-7 位的输出。
对于均匀分布的数据,我认为这是最佳的。如果您知道源代码中的零多于零,那么您可能希望将 7 位代码映射到空间的开头,以便更有可能使用 7 位代码。
简短的回答是:不,仍然没有。
我遇到了将尽可能多的信息编码为 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 或它的一些修改版本(我现在正在看Z85)会更好。
目前,如果您仅限于 ASCII 字符并且不想使用不可打印的字符,那么base91是最好的编码。它还具有编码/解码速度快如闪电的优势,因为可以使用查找表,这与必须使用慢除法解码的 base85 不同
超过base122将有助于提高效率,但它不是 8 位干净的。但是,由于它基于 UTF-8 编码,因此可以用于多种用途。现在 8-bit clean 毫无意义
请注意,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 编码。
听起来你已经有了答案,马克。UTF-8 作为二进制编码没有用处,因为任何大于一个字节的 UTF-8 字符即使存储文本(每字节 2 位或更多位)也有超过 25% 的开销。Base64编码已经比这更好了。
在Wikipedia上列出的那些旁边,有 Bommanews:
B-News(或 bommanews)的开发是为了减轻 UUEncode 和 Base64 编码固有的开销:它使用一种新的编码方法将二进制数据填充到文本消息中。这种方法消耗更多的 CPU 资源,但它设法将 UUEncode 的损失从大约 40% 降低到 3.5%(这些数字之间的小数点不是显示器上的污垢),同时仍然避免在消息中使用 ANSI 控制代码身体。
它与 yEnc 相当:来源
yEnc 比 B-News 占用更少的 CPU 资源并且达到了相同的低开销水平,但它并没有避免使用所有控制代码,它只是忽略了那些(实验上)观察到对某些人有不良影响的代码服务器,这意味着它比 B-News 更不符合 RFC。
如果您正在寻找大字母的有效编码,您可能想尝试escapeless。escapeless252 和 yEnc 都有 1.6% 的开销,但第一个它是固定的并且是预先知道的,而后者实际上在 0 到 100% 之间,具体取决于字节的分布。
我最近需要将二进制编码为 ascii,这就是我想出的。我不知道这是否是最有效的(可能不是),但它既简单又快速。基本上,我将一个字节编码为十六进制,但我没有使用基组(0-9,AF),而是使用(ap)。因为集合是连续的,所以不需要任何表查找。
//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);
}
}