【问题标题】:Do UTF-8, UTF-16, and UTF-32 differ in the number of characters they can store?UTF-8、UTF-16 和 UTF-32 可以存储的字符数是否不同?
【发布时间】:2010-09-12 22:14:02
【问题描述】:

好的。我知道这看起来像典型的“他为什么不直接谷歌它或去www.unicode.org 并查找它?” 问题,但是对于这样一个简单的问题,我在检查后仍然无法找到答案两个来源。

我很确定所有这三种编码系统都支持所有的 Unicode 字符,但我需要先确认一下,然后才能在演示文稿中做出该声明。

额外问题:这些编码在可以扩展支持的字符数上是否不同?

【问题讨论】:

    标签: unicode character-encoding utf


    【解决方案1】:

    没有 Unicode 字符可以存储在一种编码中,但不能存储在另一种编码中。这仅仅是因为有效的 Unicode 字符被限制为可以存储在 UTF-16 中的内容(它具有三种编码中最小的容量)。换句话说,UTF-8 和 UTF-32 可以用于表示比 UTF-16 更广泛的字符,但它们不是。继续阅读以了解更多详情。

    UTF-8

    UTF-8 是可变长度代码。有些字符需要 1 个字节,有些需要 2 个字节,有些需要 3 个字节,有些需要 4 个字节。每个字符的字节只是一个接一个地写入连续的字节流。

    虽然某些 UTF-8 字符可以是 4 个字节长,但 UTF-8 不能编码 2^32 个字符。它甚至不接近。我将尝试解释其中的原因。

    读取 UTF-8 流的软件只获取一个字节序列 - 它应该如何确定接下来的 4 个字节是单个 4 字节字符,还是两个 2 字节字符,还是四个 1 字节字符(或其他组合)?基本上,这是通过确定某些 1 字节序列不是有效字符、某些 2 字节序列不是有效字符等等来完成的。当这些无效序列出现时,假定它们构成更长序列的一部分。

    我敢肯定,您已经看到了一个完全不同的例子:它被称为转义。在许多编程语言中,字符串源代码中的\ 字符不会转换为字符串“编译”形式中的任何有效字符。当在源中找到 \ 时,假定它是较长序列的一部分,例如 \n\xFF。请注意,\x 是无效的 2 字符序列,\xF 是无效的 3 字符序列,但 \xFF 是有效的 4 字符序列。

    基本上,在拥有多个字符和拥有更短的字符之间需要权衡取舍。如果你想要 2^32 个字符,它们平均需要 4 个字节长。如果您希望所有字符为 2 个字节或更少,那么您不能超过 2^16 个字符。 UTF-8 提供了一个合理的折衷方案:所有ASCII 字符(ASCII 0 到 127)都被赋予 1 字节表示,这非常有利于兼容性,但允许使用更多字符。

    与大多数可变长度编码(包括上面显示的各种转义序列)一样,UTF-8 是instantaneous code。这意味着,解码器只是逐字节读取,一旦到达字符的最后一个字节,它就知道该字符是什么(并且它知道它不是一个字符的开头更长的字符)。

    例如,字符“A”使用字节 65 表示,并且没有第一个字节为 65 的二/三/四字节字符。否则解码器将无法区分这些字符'A' 后跟其他内容。

    但 UTF-8 受到了更进一步的限制。它确保较短字符的编码永远不会出现在较长字符的编码中任何地方。例如,一个 4 字节字符中的任何一个字节都不能是 65。

    由于 UTF-8 有 128 个不同的 1 字节字符(其字节值为 0-127),所有 2、3 和 4 字节字符必须仅由 128-256 范围内的字节组成。这是一个很大的限制。但是,它允许面向字节的字符串函数在很少或不需要修改的情况下工作。例如,如果 C 的 strstr() 函数的输入是有效的 UTF-8 字符串,则它总是按预期工作。

    UTF-16

    UTF-16 也是变长编码;它的字符消耗 2 或 4 个字节。 0xD800-0xDFFF 范围内的 2 字节值保留用于构造 4 字节字符,所有 4 字节字符由 0xD800-0xDBFF 范围内的两个字节和 0xDC00-0xDFFF 范围内的 2 个字节组成。因此,Unicode 不会分配 U+D800-U+DFFF 范围内的任何字符。

    UTF-32

    UTF-32 是一个固定长度的代码,每个字符有 4 个字节长。虽然这允许对 2^32 个不同的字符进行编码,但此方案中只允许 0 到 0x10FFFF 之间的值。

    容量对比:

    • UTF-8: 2,097,152(实际上是 2,166,912,但由于设计细节,其中一些映射到同一事物)
    • UTF-16: 1,112,064
    • UTF-32: 4,294,967,296(但仅限于前 1,114,112)

    因此,最受限制的是 UTF-16!正式的 Unicode 定义将 Unicode 字符限制为可以用 UTF-16 编码的字符(即范围 U+0000 到 U+10FFFF,不包括 U+D800 到 U+DFFF)。 UTF-8 和 UTF-32 支持所有这些字符。

    UTF-8 系统实际上“人为地”限制为 4 个字节。它可以扩展到 8 个字节而不违反我之前概述的限制,这将产生 2^42 的容量。最初的 UTF-8 规范实际上最多允许 6 个字节,即 2^31 的容量。但是RFC 3629 将其限制为 4 个字节,因为这是涵盖 UTF-16 的所有功能所需要的。

    还有其他(主要是历史性的)Unicode 编码方案,特别是 UCS-2(它只能将 U+0000 编码为 U+FFFF)。

    【讨论】:

    • 原始 UTF-8 的 RFC 是什么?
    • 标记的正确答案似乎完全错误。这个答案实际上给出了数字,并且解释得非常彻底。很棒的答案 +1
    • UTF-8 不能编码 2^32 个字符。它甚至不接近。 请注意,旧编码支持(如您所示)大约 2^31,如果您认为 20 亿很多,这并不接近,但在计算机软件方面只有 x2 差异相当接近...
    • 这是否意味着如果我们最多使用 8 位存储,我们可以表示大约 4.3*10^12 (2^42) 个字符?
    • 答案中第二次提到UTF-16的应该是UTF-32。我会自己编辑它,但更改太短而无法接受。感谢您的回答。
    【解决方案2】:

    所有 UTF-8/16/32 编码都可以映射所有 Unicode 字符。见Wikipedia's Comparison of Unicode Encodings

    这篇 IBM 文章 Encode your XML documents in UTF-8 非常有帮助,并指出如果您有选择,最好选择 UTF-8。主要原因是广泛的工具支持,UTF-8通常可以通过不知道 unicode 的系统。

    来自IBM article 中的规格说明部分:

    W3C 和 IETF 都有 最近变得更加坚定 首先选择 UTF-8,最后选择 UTF-8,然后 有时只是。 W3C 角色 万维网 1.0 模型: 基本原理说,“当一个独特的 字符编码是必需的, 字符编码必须是 UTF-8, UTF-16 或 UTF-32。 US-ASCII 是 向上兼容 UTF-8(一个 US-ASCII 字符串也是 UTF-8 字符串,参见 [RFC 3629]),而 UTF-8 是 因此如果兼容性合适 需要使用 US-ASCII。”在 练习,兼容 US-ASCII 非常有用,几乎是 要求。 W3C 明智地解释说, “在其他情况下,例如 API、UTF-16 或 UTF-32 可能更多 合适的。可能的原因 选择其中之一包括 内部处理的效率和 与其他的互操作性 进程。”

    【讨论】:

    【解决方案3】:

    正如大家所说,UTF-8、UTF-16 和 UTF-32 都可以编码所有 Unicode 码位。但是,UCS-2(有时被错误地称为 UCS-16)变体不能,这就是您可以找到的变体,例如在 Windows XP/Vista 中

    更多信息请参见Wikipedia

    编辑:我错了 Windows,NT 是唯一支持 UCS-2 的。但是,许多 Windows 应用程序会假设每个代码点只有一个单词,就像 UCS-2 中一样,因此您很可能会发现错误。见another Wikipedia article。 (感谢 JasonTrue)

    【讨论】:

    • 实际上 Windows XP/Vista 支持 UTF-16,但许多应用程序假定 unicode 数据是 UCS2,以防它们应该检查代理对。对于简单的情况,这通常不是问题,但对于字符迭代、插入符号的放置或截断字符串来说是一团糟。
    • 当我用 Windows 2000 进行测试时,它看起来像是使用 UCS-2。我想知道这些字体是否根本没有安装在我的 W2k 版本中......
    • @AlexisWilke,唯一知道的方法是从一个上层显示一个有效字符并希望它显示未知字符替换框 - 如果它显示两个框,它就是 UCS-2,如果只是一个然后是 UTF-16。
    • 啊!好点子。我不记得那个细节了,我不能再在我的电脑上安装 W2k 了……对于新硬件来说太旧了。另外 Ubuntu 很棒。
    【解决方案4】:

    不,它们只是不同的编码方法。它们都支持编码相同的字符集。

    UTF-8 每个字符使用 1 到 4 个字节,具体取决于您要编码的字符。 ASCII 范围内的字符只占用一个字节,而非常不寻常的字符占用四个。

    UTF-32 对每个字符使用四个字节,无论它是什么字符,因此它总是使用比 UTF-8 更多的空间来编码相同的字符串。唯一的好处是你可以通过只计算字节数来计算 UTF-32 字符串中的字符数。

    UTF-16 对大多数字符使用两个字节,对不寻常的字符使用四个字节。

    http://en.wikipedia.org/wiki/Comparison_of_Unicode_encodings

    【讨论】:

    • "所以它总是使用比 UTF-8 更多的空间" -- 你的意思是更多或相等的空间。
    • 有点不正确 - UTF-8 每个字符使用 1 到 6 个字节,具体取决于您要编码的字符。
    • @Joschua ᴜᴛꜰ‑8&ᴜᴛꜰ‑16 是 BOTH ?(?) 以找到 ?ᵗʰ 字符;只有ᴜᴛꜰ‑32 是?(?)。这个危险、普遍和有害的问题困扰着所有违反 抽象包络 的 proglangs⩙opsystems — &so code-monkeys 弄脏了他们的 FᴜᴍʙʟᴇMɪᴛᴢᴇɴ ᴜᴛꜰ‑16 个代码单元,而不是纯粹的抽象代码点。人们不断地把这件事搞砸; ᴇɢ﹕在 Java 总是使用 String.codePointCount, String.codePointAt, &ᶜ — 从不使用 String.length, String.charAt, &ᶜ。看看所有 Java 的默认值是 ꜰᴜʙᴀʀ 吗? ? 使用 ᴜᴛꜰ‑8 ‖ ᴜᴛꜰ‑32,或使用 ᴍᴀᴅ 处理 w/ᴜᴛꜰ‑16 的白话ᵗsyncracies。
    • 您可以通过计算字符串的长度来计算 UTF-32 字符串中的 代码点 的数量。这与用户感知字符不同,因为某些字符可以使用多个代码点。
    • @Arafangion UTF-8 不会为 Unicode 代码点提供 5 或 6 个字节。在 Unicode 之外编码代码点的定义已经过时并且不是标准的 UTF-8。
    【解决方案5】:

    UTF-8、UTF-16 和 UTF-32 都支持完整的 unicode 代码点集。没有一个字符支持而另一个字符不支持。

    至于附加问题“这些编码在可以扩展支持的字符数上是否不同?”是和不是。 UTF-8 和 UTF-16 的编码方式将它们可以支持的代码点总数限制在 2^32 以下。但是,Unicode 联盟不会将无法以 UTF-8 或 UTF-16 表示的代码点添加到 UTF-32。这样做会违反编码标准的精神,并且无法保证从 UTF-32 到 UTF-8(或 UTF-16)的一对一映射。

    【讨论】:

    • AFAIK,有一些方法可以扩展 UTF-8 以完全支持 32 位。使用 UTF-16,U+10FFFF 的限制是硬连线的,如果不完全改变代理对的工作方式,就无法克服。
    • 原来可以覆盖31位。这是编码方案可以处理的最大值。 (此后它已被修改为仅涵盖 Unicode 代码点,远小于 31 位。)
    • 更准确地说,原始的 UTF-8 规范允许 31 位,但后来被 RFC 3629 限制为 21 位(最高代码点限制为 U+10FFFF 而不是 U+1FFFFF)以维护完全兼容 UTF-16 编码,而不是 Unicode 本身。
    【解决方案6】:

    如果有疑问,我个人总是会检查 Joel's post 的有关 unicode、编码和字符集的信息。

    【讨论】:

    • 为什么不转而查看 unicode.org,这得益于对事情的实际正确性。
    • Joel 的帖子并非旨在作为 unicode、编码、字符集或任何此类内容的参考。相反,它是一篇说明您必须了解的内容。
    • @JonHanna 你能澄清一下 Joel 帖子的哪一部分不正确吗?
    猜你喜欢
    • 2020-01-28
    • 2011-02-10
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 2015-05-02
    • 2019-07-31
    • 2016-05-31
    相关资源
    最近更新 更多