为什么有必要在UTF-8中标记连续字节?

时间:2016-08-12 22:11:01

标签: unicode utf-8 character-encoding utf

我最近一直在阅读UTF-8可变宽度编码,我发现奇怪的是UTF-8指定每个连续字节的前两位为10。

 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中的延续位真的那么重要吗?第二种编码看起来效率更高。

3 个答案:

答案 0 :(得分:8)

UTF-8是自我验证的,快速前进,更容易后退。

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

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

更容易后退:如果必须向后读取字节,您可以立即通过10xxxxxx识别延续字符。然后,您将能够向后扫描10xxxxxx前导字节的11xxxxxx字节,而无需扫描超过前导字节。

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

除了已经提到的易于迭代之外:UTF-8旨在安全地使用基于ASCII(和其他UTF-8-unaware)的工具来处理搜索,连接,替换和转义等常见操作。 / p>

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

  

80 - 407f | 1xxxxxx 0xxxxxxx

因此,有一些东亚多字节编码就是这样做的,有一些不幸的结果,UTF-8特别试图避免。

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

在将用户输入结合到控制流中的所有内容中提示安全漏洞。查询中的SQL注入,网页上的HTML注入,shell脚本中的命令注入等。

(东亚多字节编码并不像这里的编码那么糟糕,因为它们没有将ASCII控制代码重用为连续字节。正如所提出的,使用这种编码的文本不能例如,存储在一个以C结尾的字符串中。然而,Shift-JIS和朋友造成了大量的安全漏洞,我们都很高兴摆脱它们。)