【问题标题】:What issues would come from treating UTF-16 as a fixed 16-bit encoding?将 UTF-16 视为固定的 16 位编码会出现什么问题?
【发布时间】:2011-02-21 13:28:00
【问题描述】:

我正在阅读一些关于 Unicode 的 SO 问题,其中有一些我不完全理解的 cmets,比如这个:

Dean Harding: UTF-8 是一个 可变长度编码,即 比处理更复杂 定长编码。另外,看看我的 cmets 对 Gumbo 的回答:基本上, 组合字符存在于所有 编码(UTF-8、UTF-16 和 UTF-32)和 它们需要特殊处理。你可以 使用与您相同的特殊处理 用于组合字符也 处理 UTF-16 中的代理对,所以 在大多数情况下,您可以忽略 代理和对待 UTF-16 就像 一个固定的编码。

我对最后一部分(“大部分”)有点困惑。如果 UTF-16 被视为固定的 16 位编码,这会导致什么问题? BMP 之外有字符的可能性有多大?如果存在,如果您假设为两字节字符,这会导致什么问题?

我在 Surrogates 上阅读了 Wikipedia 信息,但它并没有真正让我更清楚!

编辑:我想我真正的意思是“为什么有人建议将 UTF-16 视为虚假的固定编码?”

编辑2:

我在“Is there any reason to prefer UTF-16 over UTF-8?”中发现了另一条评论,我认为这更好地解释了这一点:

Andrew Russell: 性能方面: UTF-8 比 UTF-8 更难解码 UTF-16。在 UTF-16 中,字符是 一个基本的多语言平面 字符(2 个字节)或代理项 对(4 个字节)。 UTF-8 字符可以 介于 1 到 4 个字节之间

这表明 UTF-16 不会有任何三字节字符,因此假设 16 位,您不会因为结束一字节而“完全搞砸”。但我仍然不相信这与假设 UTF-8 是单字节字符有什么不同!

【问题讨论】:

    标签: unicode utf-16 unicode-string


    【解决方案1】:

    UTF-16 包括所有"base plane" characters。 BMP 涵盖了大多数当前的书写系统,并包括许多人们可以实际遇到的旧字符。看看它们,然后决定你是否真的会遇到扩展位面中的任何字符:楔形文字、炼金术符号等。很少有人会真正想念它们。

    如果您仍然遇到需要扩展平面的字符,这些字符由两个代码点(代理项)编码,您会看到两个空方块或问号,而不是这样的非字符。 UTF 是自同步的,因此代理字符的一部分永远不会看起来像合法字符。这使得字符串搜索之类的事情即使存在代理项并且您不处理它们也可以工作。

    因此,除了您不处理扩展字符这一事实之外,将 UTF-16 视为有效的 USC-2 所产生的问题很少。

    编辑: Unicode 使用在前一个字符的空格处呈现的“组合标记”,如重音符号、波浪号、抑扬符等。有时变音符号与字母的组合可以表示为一个不同的代码点,例如á 可以表示为单个 \u00e1,而不是简单的 'a' + 重音符号 \u0061\u0301。您仍然不能将像 这样的不寻常组合表示为一个代码点。这使得搜索和拆分算法更加复杂。如果您以某种方式使您的字符串数据统一(例如,仅使用纯字母和组合标记),搜索和拆分再次变得简单,但无论如何您都会丢失 '一个位置是一个字符' 属性。如果您正在认真排版并且想要明确存储像 fi 这样的连字,其中一个代码点对应于 2 或 3 个字符,则会出现对称问题。这不是 UTF 问题,而是一般的 Unicode 问题,AFAICT。

    【讨论】:

    • +1 以获得非常实用的答案。我也同意,在许多情况下,支持 utf32 或真正的可变宽度 utf16 的额外努力是不值得的。会感到不安的用户已经习惯了解决这个问题。无论如何,简单性是我们使用 Unicode 的主要原因之一。
    • 这取决于您开始使用的字符串类型。 UTF-16 是 UCS-2 最简单的升级路径,但 8 位字符最难升级路径。
    【解决方案2】:

    重要的是要了解,即使是 UTF-32 也是固定长度的代码点,而不是字符。有许多字符是由多个代码点组成的,因此您不可能真正拥有一个数字(代码单元)对应一个字符(用户感知的)的 Unicode 编码。

    回答您的问题 - 将 UTF-16 视为固定长度编码形式的最明显问题是在代理对中间断开一个字符串,从而获得两个无效的代码点。这完全取决于您对文本的处理方式。

    【讨论】:

      【解决方案3】:

      我想我真正的意思是 “为什么有人会建议治疗 UTF-16 似乎是固定编码 假的?”

      两个词:向后兼容性。

      Unicode 最初旨在使用固定宽度的 16 位编码 (UCS-2),这就是为什么 Unicode 的早期采用者(例如,使用 Java 的 Sun 和使用 Windows NT 的 Microsoft)使用 16 位字符类型的原因.当事实证明 65,536 个字符对每个人来说都不够用时,开发了 UTF-16 是为了让这个 16 位字符系统能够代表 16 个新的“平面”。

      这意味着字符不再是固定宽度的,因此人们创造了“没关系,因为 UTF-16几乎是固定宽度”的合理化解释。

      但我仍然不相信这是 与假设 UTF-8 有什么不同 单字节字符!

      严格来说,它没有任何不同。对于"\uD801\uDC00".lower() 之类的内容,您会得到不正确的结果。

      但是,假设 UTF-16 是固定宽度的,比假设 UTF-8 是固定宽度的更不可能中断。非 ASCII 字符在英语以外的语言中非常常见,但非 BMP 字符非常少见。

      您可以使用相同的特殊处理 用于组合字符的 也处理代理对 UTF-16

      我不知道他在说什么。组合序列的组成字符具有单独的身份,与代理字符完全不同,代理字符只是成对有意义。

      特别是,组合序列中的字符可以一次转换为一个字符的不同编码。

      >>> 'a'.encode('UTF-8') + '\u0301'.encode('UTF-8')
      b'a\xcc\x81'
      

      但不是代理:

      >>> '\uD801'.encode('UTF-8') + '\uDC00'.encode('UTF-8')
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      UnicodeEncodeError: 'utf-8' codec can't encode character '\ud801' in position 0: surrogates not allowed
      

      【讨论】:

      • 感谢您提供更多信息。这一点,特别是:“但是,假设 UTF-16 是固定宽度比假设 UTF-8 是固定宽度更不可能中断。非 ASCII 字符在英语以外的语言中非常常见,但非 BMP 字符是很稀少。”。我认为这个想法是“如果你真的会变得懒惰,至少要以一种不太可能引起问题的方式去做”
      • OTOH,可以令人信服地辩称,50% 正确的程序比 99% 正确的程序更接近于修复。
      • “组合序列,其组成字符具有单独的身份,与代理字符完全不同,代理字符只能成对有意义。”——从概念上讲这是正确的,但实际上区别通常并不重要.例如,文本渲染库可以简单地将代理对视为连字(本质上将一系列代码单元渲染为一个字形),即使它们是完全不同的概念。 Unicode 标准中甚至提到了这种“肮脏的技巧”,因此 if 方法绝对没有错,它会导致正确的结果。
      【解决方案4】:

      UTF-16 是一种可变长度编码。较旧的 UCS-2 不是。如果您将可变长度编码视为固定(恒定长度),那么每当您使用“16 位数字的数量”来表示“字符数”时,您就有可能引入错误,因为字符数实际上可能小于16 位数量。

      【讨论】:

      • 我想我的问题措辞很糟糕。我真正想了解的是为什么人们会建议将 UTF-16 视为固定编码,而这似乎会带来问题。
      猜你喜欢
      • 2010-10-26
      • 2015-01-28
      • 2012-03-16
      • 1970-01-01
      • 2012-04-01
      • 2012-03-13
      • 2022-01-23
      • 1970-01-01
      • 2012-06-30
      相关资源
      最近更新 更多