我想在XML文件中编码和解码二进制数据(使用Python,但无论如何)。我必须面对XML标记内容具有非法字符的事实。唯一允许的内容在XML specs中描述:
Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
这意味着不允许的是:
1个字节可以编码256种可能。有了这些限制,第一个字节限制为256-29-8-1-3 = 215个可能性。
在第一个字节的215个可能性中,this post仅使用64个可能性。 Base64产生33%的开销(一旦用base64编码,6位变为1字节)。
所以我的问题很简单:是否有比base64更有效的算法来编码XML中的二进制数据?如果没有,我们应该从哪里开始创建它?(库等)
注意:你不会回答这个帖子“你不应该使用XML来编码二进制数据,因为......”。只是不要。你最多可以争论为什么不使用215种可能性来支持糟糕的XML解析器。
NB2:我不是在谈论第二个字节,但是当我们使用补充的Unicode平面时,肯定会有一些关于可用性的事项以及它应该从10xxxxxx开始遵守UTF8标准的事实。如果没有?)。答案 0 :(得分:7)
感谢Aya的Asci85链接,有很好的想法。
我在下面为我们的案例开发了它们。
对于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 specs允许使用除0x00 ...
之外的控制字符对于2字节字符(110xxxxx 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 101xxxxx 10xxxxxx)= -2 ^ 11 我不会对4字节字符进行计算,这是毫无意义的:可能的数量可以忽略不计,并且在此范围内有太多非法的UTF-8字符。
因此,让我们看看我们可以在3字节(24位)空间上进行哪些组合:
还有其他可能性(比如一个3字节的字符结尾或者在空格中开始,但是像4字节字符一样,很难评估(对我来说)并且可能忽略不计)。
可能性总数:
这意味着多少开销?
18% overhead
。方式优于Base64的33%开销和Ascii85的25%开销!编辑:这是针对XML1.0规范的。使用XML1.1(不广泛支持...),理论开销为12.55%。我设法制作了一个二进制安全算法,XML1.1的开销为14.7%。
坏消息是,如果不使用大字典"我们就不能轻易获得18%的成功率。 (即长期套装)。但它容易获得20%,而且非常容易但却不太可行,只能获得19%。
良好的编码长度候选人:
注意:0,84是平均值"有用性"空位(20,32 / 24)。
我们需要建立一个" dictionnary"这将绘制"空间可能性" (长度为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%的开销。
这是这个问题的下一个挑战。谁想玩? :)
要编码的20个二进制位:ABCDEFGHIJKLMNOPQRST
产生的名为"编码":24位长的UTF-8字符串
数学编码算法(不基于任何已知的编程语言):
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的倍数时,该算法还没有提供管理字符串终止的方法。还必须提供解码算法,但这很容易(只是不要忘记抛出异常以强制解码的单一性。)
答案 1 :(得分:2)
我用C代码开发了这个概念。
该项目位于GitHub上,最终称为BaseXML:https://github.com/kriswebdev/BaseXML
它有20%的开销,这对二进制安全版本很有用。
我很难与Expat一起工作,后者是Python的场景XML解析器(THAT DOESN&#39; T支持XML1.1!)。因此,您将找到适用于XML1.0的BaseXML1.0二进制安全版本。
我可能会发布&#34; for XML1.1&#34;如果请求版本稍后(它也是二进制安全的并且具有14.7%的开销),它已经准备好并且在Python内置的XML解析器中工作确实无用,所以我不想让太多的人混淆版本(还)。
答案 2 :(得分:1)
更糟糕的是:您实际上并没有可以使用的215个不同的字节值。生成的二进制数据必须在XML表示的任何编码中有效(几乎可以肯定是UTF-8),这意味着禁止许多字节序列。 0xc2后跟0x41只是一个随机的例子。 XML是文本(Unicode字符序列),而不是二进制数据。传输时,它使用一些编码(几乎是alwats UTF-8)进行编码。如果你试图将它视为二进制数据,那么在我看来,你要求的方式比它值得处理的更麻烦。
如果你还想这样做......
XML是文本。所以我们不要试图将二进制数据编码为二进制数据。这不会导致一种简单或明显的方式将其展示到XML文档中。让我们尝试将二进制数据编码为文本!
让我们尝试一种非常简单的编码:
这意味着你只使用1到16号平面的字符。所有限制字符都在0号平面(BMP)中,所以你在这里很安全。
然后,当您将此XML文档编码为UTF-8进行传输时,每个字符都需要4个字节进行编码。因此,每20位原始数据消耗32位,与原始数据的纯二进制编码相比,这是60%的开销。 这比base64的33%差,这让它成为一个糟糕的主意。
这种编码方案有点浪费,因为它不使用BMP字符。我们可以使用BMP字符使其更好吗?不是琐碎的。 20是我们可以用于组的最大尺寸(log(0x10FFFF) ~ 20.09
)。我们可以重新映射方案,尽可能使用一些manu BMP字符,因为这些字符占用较少的空间来编码UTF-8,但这不仅会使编码复杂化很多(禁用的字符也是分散的,所以我们有几种情况要处理但它只能导致约6.25%的位模式(BMP中的Unicode字符的一小部分)得到改善,而对于6.25%的大部分,我们只保存一个字节。对于随机数据,开销从60%降低到55%左右。 结果仍然比base64差得多,除了一些非常人为的数据。请注意,开销与数据有关。对于0.2%的位模式,您实际上将获得压缩而不是开销(0.012%的模式为60%压缩,0.18%的模式为20%压缩)。但这些分数真的很低。这不值得。
换句话说:如果你想用4字节UTF-8序列编码任何东西,你需要每个序列使用32位(当然),但这些位中有11位是固定的且不可更改的:这些位必须适合模式11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
并且那里只有21个x
。 60%的开销内置于UTF-8中,因此如果您想将此作为任何编码的基础来改善base64的开销,那么您就是从后面开始的!
我希望这可以说服你使用任何此类方案都无法提高base64的密度。