我真的对 Unicode 中的 UTF 感到困惑。
有 UTF-8、UTF-16 和 UTF-32。
我的问题是:
支持所有 Unicode 块的 UTF 是什么?
什么是最好的 UTF(性能、大小等),为什么?
这三个 UTF 有什么不同?
什么是字节序和字节顺序标记(BOM)?
谢谢
支持所有 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 的好处
UTF-16 的好处
UTF-32 的好处
它们都支持所有 Unicode 代码点。
它们具有不同的性能特征——例如,UTF-8 对 ASCII 字符更紧凑,而 UTF-32 更容易处理整个 Unicode,包括基本多语言平面之外的值(即高于 U+FFFF)。由于每个字符的宽度可变,UTF-8 字符串很难用于获取二进制编码中的特定字符索引 - 你已经扫描过了。UTF-16 也是如此,除非您知道没有非 BMP 字符。
字节序决定(对于 UTF-16 和 UTF-32)是最高有效字节在前,最低有效字节在最后,反之亦然。例如,如果您想用 UTF-16 表示 U+1234,则可以是 { 0x12, 0x34 } 或 { 0x34, 0x12 }。字节顺序标记指示您正在处理的字节序。UTF-8 没有不同的字节顺序,但在文件开头看到 UTF-8 BOM 可以很好地表明它是UTF-8。
这里有一些很好的问题,并且已经有几个很好的答案。我也许可以添加一些有用的东西。
如前所述,所有三个都涵盖了完整的可能代码点集,从 U+0000 到 U+10FFFF。
取决于文本,但这里有一些可能感兴趣的细节。UTF-8 每个字符使用 1 到 4 个字节;UTF-16 使用 2 或 4;UTF-32 总是使用 4。需要注意的一个有用的事情是这个。如果您使用 UTF-8,那么英文文本将被编码为绝大多数字符,每个字符一个字节,但中文每个字符需要 3 个字节。使用 UTF-16,英文和中文都需要 2。所以基本上 UTF-8 是英文的胜利;UTF-16 是中文的胜利。
主要区别在上面对 #2 的回答中提到,或者正如 Jon Skeet 所说,请参阅 Wikipedia 文章。
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 开头,我们知道它是小端。
不确定我是否在其他答案中添加了任何内容,但我发现英文与中文的具体分析有助于过去向其他人解释这一点。
一种看待它的方式是规模大于复杂性。通常,它们增加了对文本进行编码所需的字节数,但降低了解码它们用来表示字符的方案的复杂性。因此,UTF-8 通常很小但解码起来可能很复杂,而 UTF-32 占用更多字节但易于解码(但很少使用,UTF-16 更常见)。
考虑到这一点,通常选择 UTF-8 进行网络传输,因为它的大小更小。而在更容易解码比存储大小更重要的情况下,选择了 UTF-16。
BOM 旨在作为文件开头的信息,描述使用了哪种编码。但是,这些信息经常丢失。
Joel Spolsky wrote a nice introductory article about Unicode: