2016-08-12 54 views
2

我最近一直阅读了对UTF-8可变宽度编码,并且我觉得很奇怪,UTF-8指定每延续字节的前两个比特是10,为什么需要在UTF-8中标记连续字节?

Range   | Encoding 
-----------------+----------------- 
    0 - 7f  | 0xxxxxx 
    80 - 7ff  | 110xxxx 10xxxxxx 
    800 - ffff | 1110xxx 10xxxxxx 10xxxxxx 
10000 - 10ffff | 11110xx 10xxxxxx 10xxxxxx 10xxxxxx 

我玩弄其他可能的可变宽度编码,并发现通过使用以下方案,最多需要3个字节来存储所有的Unicode。如果第一位是1,则字符至少再多加一个字节(读取直到第一位为0)。

Range   | Encoding 
-----------------+----------------- 
    0 - 7f  | 0xxxxxx 
    80 - 407f | 1xxxxxx 0xxxxxxx 
    4080 - 20407f | 1xxxxxx 1xxxxxxx 0xxxxxxx 

UTF-8中的延续位真的很重要吗?第二种编码看起来效率更高。

+1

UTF-8方案允许您恢复预期的解码来自*任何*代码单元的位置。 –

+0

即使使用我的编码,这是可能的。从任意位置开始,直到找到一个结束字节(带有0),然后下一个字节是该字符的开始。 –

+2

并非如此:如果您指向'0xxxxxx'字节,则无法判断这是一个完整的字符还是多字节序列的损坏结尾。 (假设我不能后退。) –

回答

6

UTF-8具有自我验证功能,可以快速前进,并且更容易后退。

自我验证:由于序列中的第一个字节指定了长度,所以下一个X字节必须适合10xxxxxx,否则您的序列无效。看到一个10xxxxxx字节本身立即可识别为无效。
您建议的编码没有验证内置。

快速前进:如果必须跳过该字符,则可以立即跳过由第一个字节确定的X个字节,而不必检查每个中间字节。

更容易倒退:如果您必须向后读取字节,您可以立即通过10xxxxxx识别出连续字符。然后,您可以向后扫描10xxxxxx字节的11xxxxxx前导字节,而不必扫描前导字节。

请参阅UTF-8 Invalid byte sequences的维基百科。

2

使用您提出的方案,如果您查看编码为0xxxxxxx的字节,则无法确定它是单字节单元0x00..0x7F还是多字节单元的最后一个字节。您必须向后扫描并查看前面的字节才能知道(并且您必须向后检查两个单元以查看它是否是2或3字节代码点的最后一个字节)。如果您有1xxxxxxx字节,则无法确定它是多字节单元的第一个字节还是中间字节。同样,你必须向后扫描。

相比之下,UTF-8方案允许您告诉任何非连续字节有多少个后续字节是代码点的一部分。对于连续字节,您只需要向后扫描到起始字节。你也会得到错误检查; UTF-8中有很多无效序列,这实际上是一个好处。 (字节0xC0,0xC1,0xF5..0xFF不能出现在有效的UTF-8中)。

2

除了已经提到的简化迭代之外:UTF-8旨在保证基于ASCII的(以及其他UTF-8 -unaware)工具来处理搜索,连接,替换和转义等常见操作。

ASCII兼容互操作性和安全性的优势超过了使用字符U + 0800到U + 407F的额外字节的成本。

80 - 407f | 1xxxxxx 0xxxxxxx

因此,有一些东亚多字节编码做到了这一点,UTF-8专门设法避免了一些不幸的结果。

在这个提议的方案中,连续字节现在与ASCII重叠,许多ASCII字符对不同的语言和工具有特殊的含义。因此,如果你想说¢这是0x80,0x27,并且第二个字节看起来像一个"任何工具,操纵字节字符串不支持,并知道这个数据使用,建议的编码。

在将用户输入组合成控制流的所有内容中都提供了安全漏洞。查询中的SQL注入,网页上的HTML注入,shell脚本中的命令注入等等。因为他们没有将ASCII控制代码作为连续字节重用,所以东亚多字节编码并不像这种编码那样糟糕,正如所提出的,使用这种编码的文本不能存储在C例如,以空字符结尾的字符串,但Shift-JIS和朋友造成了一大堆安全漏洞,我们都很高兴能够摆脱它们。)

相关问题