11

我真的对 Unicode 中的 UTF 感到困惑。

有 UTF-8、UTF-16 和 UTF-32。

我的问题是:

  1. 支持所有 Unicode 块的 UTF 是什么?

  2. 什么是最好的 UTF(性能、大小等),为什么?

  3. 这三个 UTF 有什么不同?

  4. 什么是字节序和字节顺序标记(BOM)?

谢谢

4

6 回答 6

29

支持所有 Unicode 块的 UTF 是什么?

所有 UTF 编码都支持所有 Unicode 块 - 没有 UTF 编码不能代表任何 Unicode 代码点。但是,一些非 UTF、较旧的编码,例如 UCS-2(类似于 UTF-16,但缺少代理对,因此缺乏对高于 65535/U+FFFF 的代码点进行编码的能力)可能不会。

什么是最好的 UTF(性能、大小等),为什么?

对于主要是英语和/或只是 ASCII 的文本数据,UTF-8 是迄今为止最节省空间的。然而,UTF-8 有时不如 UTF-16 和 UTF-32 节省空间,因为大多数使用的代码点都很高(例如大量的 CJK 文本)。

这三个 UTF 有什么不同?

UTF-8 将每个 Unicode 代码点编码为一到四个字节。Unicode 值 0 到 127 与它们在 ASCII 中的值相同,它们的编码方式与它们在 ASCII 中的一样。值为 128 到 255 的字节用于多字节代码点。

UTF-16 将每个 Unicode 代码点编码为两个字节(一个 UTF-16 值)或四个字节(两个 UTF-16 值)。基本多语言平面中的任何内容(Unicode 代码点 0 到 65535,或 U+0000 到 U+FFFF)都使用一个 UTF-16 值进行编码。来自更高平原的代码点通过一种称为“代理对”的技术使用两个 UTF-16 值。

UTF-32 不是 Unicode 的可变长度编码;所有 Unicode 代码点值都按原样编码。这意味着它U+10FFFF被编码为0x0010FFFF.

什么是字节序和字节顺序标记(BOM)?

字节序是一段数据、特定 CPU 架构或协议如何对多字节数据类型的值进行排序。Little-endian 系统(例如 x86-32 和 x86-64 CPU)将最低有效字节放在第一位,而 big-endian 系统(例如 ARM、PowerPC 和许多网络协议)将最高有效字节放在第一位。

在 little-endian 编码或系统中,32 位值0x12345678存储或传输为0x78 0x56 0x34 0x12. 在大端编码或系统中,它被存储或传输为0x12 0x34 0x56 0x78.

在 UTF-16 和 UTF-32 中使用字节顺序标记来指示文本将被解释为哪种字节序。Unicode 以一种巧妙的方式做到了这一点——U+FEFF 是一个有效的代码点,用于字节顺序标记,而 U+FFFE 不是。因此,如果文件以 开头0xFF 0xFE,则可以假定文件的其余部分以 little-endian 字节顺序存储。

UTF-8 中的字节顺序标记在技术上是可行的,但由于显而易见的原因,在字节序的上下文中是没有意义的。但是,以 UTF-8 编码的 BOM 开头的流几乎可以肯定地暗示它是 UTF-8,因此可以用于识别。

UTF-8 的好处

  • ASCII 是 UTF-8 编码的子集,因此是将 ASCII 文本引入“Unicode 世界”的好方法,无需进行数据转换
  • UTF-8 文本是 ASCII 文本最紧凑的格式
  • 有效的 UTF-8 可以按字节值排序并产生排序的代码点

UTF-16 的好处

  • UTF-16 比 UTF-8 更容易解码,即使它是可变长度编码
  • 对于 BMP 中的字符,UTF-16 比 UTF-8 更节省空间,但在 ASCII 之外

UTF-32 的好处

  • UTF-32 不是可变长度的,因此它不需要特殊的逻辑来解码
于 2011-07-30T09:41:01.110 回答
18
于 2011-07-30T15:42:02.730 回答
6
  1. 它们都支持所有 Unicode 代码点。

  2. 它们具有不同的性能特征——例如,UTF-8 对 ASCII 字符更紧凑,而 UTF-32 更容易处理整个 Unicode,包括基本多语言平面之外的值(即高于 U+FFFF)。由于每个字符的宽度可变,UTF-8 字符串很难用于获取二进制编码中的特定字符索引 - 你已经扫描过了。UTF-16 也是如此,除非您知道没有非 BMP 字符。

  3. 查看UTF-8UTF-16UTF-32的维基百科文章可能是最容易的

  4. 字节序决定(对于 UTF-16 和 UTF-32)是最高有效字节在前,最低有效字节在最后,反之亦然。例如,如果您想用 UTF-16 表示 U+1234,则可以是 { 0x12, 0x34 } 或 { 0x34, 0x12 }。字节顺序标记指示您正在处理的字节序。UTF-8 没有不同的字节顺序,但在文件开头看到 UTF-8 BOM 可以很好地表明它UTF-8。

于 2011-07-30T09:39:10.580 回答
3

这里有一些很好的问题,并且已经有几个很好的答案。我也许可以添加一些有用的东西。

  1. 如前所述,所有三个都涵盖了完整的可能代码点集,从 U+0000 到 U+10FFFF。

  2. 取决于文本,但这里有一些可能感兴趣的细节。UTF-8 每个字符使用 1 到 4 个字节;UTF-16 使用 2 或 4;UTF-32 总是使用 4。需要注意的一个有用的事情是这个。如果您使用 UTF-8,那么英文文本将被编码为绝大多数字符,每个字符一个字节,但中文每个字符需要 3 个字节。使用 UTF-16,英文和中文都需要 2。所以基本上 UTF-8 是英文的胜利;UTF-16 是中文的胜利。

  3. 主要区别在上面对 #2 的回答中提到,或者正如 Jon Skeet 所说,请参阅 Wikipedia 文章。

  4. Endianness:对于 UTF-16 和 UTF-32,这是指字节出现的顺序;例如,在 UTF-16 中,字符 U+1234 可以编码为 12 34(大端)或 34 12(小端)。BOM 或字节顺序标记很有趣。假设您有一个以 UTF-16 编码的文件,但您不知道它是大端还是小端,但您注意到文件的前两个字节是 FE FF。如果这是大端,则字符将是 U+FEFF;如果是小端,则表示 U+FFFE。但事情是这样的:在 Unicode 中,代码点 FFFE 是永久未分配的:那里没有字符!因此我们可以知道编码必须是大端的。FEFF 字符在这里是无害的;它是零宽度无中断空间(基本上是不可见的)。同样,如果文件以 FF FE 开头,我们知道它是小端。

不确定我是否在其他答案中添加了任何内容,但我发现英文与中文的具体分析有助于过去向其他人解释这一点。

于 2011-07-30T09:53:47.793 回答
2

一种看待它的方式是规模大于复杂性。通常,它们增加了对文本进行编码所需的字节数,但降低了解码它们用来表示字符的方案的复杂性。因此,UTF-8 通常很小但解码起来可能很复杂,而 UTF-32 占用更多字节但易于解码(但很少使用,UTF-16 更常见)。

考虑到这一点,通常选择 UTF-8 进行网络传输,因为它的大小更小。而在更容易解码比存储大小更重要的情况下,选择了 UTF-16。

BOM 旨在作为文件开头的信息,描述使用了哪种编码。但是,这些信息经常丢失。

于 2011-07-30T09:40:43.997 回答
2

Joel Spolsky wrote a nice introductory article about Unicode:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

于 2011-07-30T15:58:37.397 回答