感谢 Aya 提供 Asci85 链接,有很好的想法。
我在下面为我们的案例开发了它们。
UTF-8 字符可能:
对于 1 字节字符 (0xxxxxxx):每字节 96 个可能性
+
UTF-8 ASCII 字符 0xxxxxxx = +2^7
-
UTF-8 控制字符 000xxxxx = -2^5
+
XML 允许的 UTF-8 控制字符 (00000009, 0000000A, 0000000D) = +3
-
XML 实体不允许的字符(<、>、&)= -3
编辑:这是针对 XML1.0 规范的。XML 1.1 规范允许使用除 0x00 之外的控制字符...
对于 2 字节字符 (110xxxxxx 10xxxxxx):每 2 字节 1920 个可能性
+
UTF-8 2 字节字符 110xxxxx 10xxxxxx = +2^11
-
UTF-8 非法非规范字符 (1100000x 10xxxxxx) = -2^7
对于 3 字节字符 (1110xxxx 10xxxxxx 10xxxxxx):每 3 字节 61440 个可能性
+
UTF-8 3 字节字符 1110xxxx 10xxxxxx 10xxxxxx = +2^16
-
UTF-8 非法非规范字符 (11100000 100xxxxx 10xxxxxx) = -2^11
-
Unicode 保留的 UTF-16 代码点 (11101101 101xxxxxx 10xxxxxx) = -2^11
而且我不会对 4 字节字符进行计算,这是没有意义的:可能的数量可以忽略不计,并且此范围内的非法 UTF-8 字符太多。
比方说 3 字节空间中的编码可能性
那么让我们看看我们可以在 3 字节(24 位)空间上进行哪些组合:
- 0xxxxxxx 0xxxxxxx 0xxxxxxx : 那是 96*96*96 = 884736 种可能性
- 0xxxxxxx 110xxxxx 10xxxxxx : 那是 96*1920 = 184320 种可能性
- 110xxxxx 10xxxxxx 0xxxxxxx : 那是 1920*96 = 184320 种可能性
- 1110xxxx 10xxxxxx 10xxxxxx :那是 61440 = 61440 种可能性
还有其他可能性(比如 3 字节字符在空间中结束或开始,但像 4 字节字符,这将难以评估(对我而言)并且可能可以忽略不计)。
可能性总数:
- 一个 3 字节的空间有 2^24 = 16777216 种可能性。
- 该空间中的 UTF-8 兼容可能性为 884736+2*184320+61440 = 1314816 种可能性。
这意味着多少开销?
- 24 位空间可用位:Log2(16777216)=24(当然!这是为了数学理解)
- 该空间的 UTF-8 有用位:Log2(1314816)=20,32 个有用位。
- 这意味着我们需要 24 位空间来编码 20,32 位有用信息,即。最小的理论开销是
18% overhead
. 比 Base64 的 33% 开销和 Ascii85 的 25% 开销要好得多!
编辑:这是针对 XML1.0 规范的。使用 XML1.1(未得到广泛支持...),理论开销为 12.55%。我设法为 XML1.1 创建了一个开销为 14.7% 的二进制安全算法。
如何接近这 18% 的开销?
坏消息是,如果不使用大型“字典”(即长尾集),我们将无法轻易获得 18% 的开销。但是获得 20%很容易,获得 19% 很容易但不太实用。
良好的编码长度候选:
- 6 位可以编码 5 位,开销为 20% (2^(6*0,84) > 2^5)
- 12 位可以编码 10 位,开销为 20% (2^(12*0,84) > 2^10)
- 24 位可以编码 20 位,开销为 20% (2^(24*0,84) > 2^20)
- 25 位可以编码 21 位,开销为 19% (2^(25*0,84) > 2^21)
注意:0,84 是空间位 (20,32/24) 的平均“有用性”。
如何构建我们的编码算法?
我们需要构建一个“字典”,它将“可能的空间”(长度为 5、10、20 或 21 位的随机位序列,取决于为算法选择的编码长度 - 只需选择一个)映射到 utf8-兼容序列(相应的长度为 6、12、24 或 25 位的 utf8 位序列)。
最简单的起点是将 20 位序列编码为 24 位兼容的 UTF-8 序列:这正是上面用于计算可能性的示例,它有 3 个 UTF-8 字节长(所以我们不必担心未终止UTF8 字符)。
请注意,我们必须使用 2 字节(或以上)的 UTF-8 字符编码空间来达到 20% 的开销。只有 1 字节长的 UTF8 字符,我们只能使用 RADIX-24 达到 25% 的开销。但是,3 字节长的 UTF-8 字符无需达到 20% 的开销。
这是这个问题的下一个挑战。谁想玩?:)
一种算法的提议,我将 XML 命名为 BaseUTF-8
20 个二进制位进行编码:ABCDEFGHIJKLMNOPQRST
生成的名为“encoded”的 UTF-8 字符串:24 位长
数学编码算法(不基于任何已知的编程语言):
If GH != 00 && NO != 00:
encoded = 01ABCDEF 0GHIJKLM 0NOPQRST # 20 bits to encode, 21 space bits with restrictions (1-byte UTF-8 char not starting by 000xxxxx ie ASCII control chars)
If ABCD != 0000:
If GH == 00 && NO == 00: # 16 bits to encode
encoded = 0010ABCD 01EFIJKL 01MPQRST
Else If GH == 00: # 18 bits to encode, 18 space bits with restrictions (1-byte UTF-8 ASCII control char, 2-bytes UTF-8 char noncanonical)
encoded = 0NOFIJKL 110ABCDE 10MPQRST
Else If NO == 00: # 18 bits to encode
encoded = 110ABCDE 10MPQRST 0GHFIJKL
If ABCD == 0000: # 16 bits to encode
encoded = 0011EFGH 01IJKLMN 01OPQRST
On "encoded" variable apply:
convert < (0x3C) to Line Feed (0x0A)
convert > (0x3E) to Cariage Return (0x0D)
convert & (0x26) to TAB (0x09)
这就是您仅获得 20% 开销的方式。
当要编码的字符串不是 20 的倍数时,该算法还没有提供管理字符串终止的方法。还必须提供解码算法,但这很容易(只是不要忘记抛出异常强制解码的唯一性)。