584

UTF-8、UTF-16 和 UTF-32 之间有什么区别?

我知道它们都将存储 Unicode,并且每个都使用不同数量的字节来表示一个字符。选择一个比另一个有优势吗?

4

14 回答 14

454

UTF-8 在 ASCII 字符代表文本块中的大多数字符的情况下具有优势,因为 UTF-8 将这些字符编码为 8 位(如 ASCII)。另一个优点是仅包含 ASCII 字符的 UTF-8 文件与 ASCII 文件具有相同的编码。

UTF-16 在 ASCII 不占优势的情况下更好,因为它主要使用每个字符 2 个字节。UTF-8 将开始为高阶字符使用 3 个或更多字节,而 UTF-16 对大多数字符仅保留 2 个字节。

UTF-32 将覆盖 4 个字节中所有可能的字符。这使它非常臃肿。我想不出使用它有什么好处。

于 2009-01-30T17:15:19.197 回答
382

简而言之:

  • UTF-8:可变宽度编码,向后兼容 ASCII。ASCII 字符(U+0000 到 U+007F)占用 1 个字节,代码点 U+0080 到 U+07FF 占用 2 个字节,代码点 U+0800 到 U+FFFF 占用 3 个字节,代码点 U+10000 到 U+10FFFF占用 4 个字节。适合英文文本,不适合亚洲文本。
  • UTF-16:可变宽度编码。代码点 U+0000 到 U+FFFF 占用 2 个字节,代码点 U+10000 到 U+10FFFF 占用 4 个字节。不适合英文文本,适合亚洲文本。
  • UTF-32:固定宽度编码。所有代码点占用四个字节。一个巨大的内存猪,但操作起来很快。几乎没有使用过。

长篇大论:参见维基百科:UTF-8UTF-16UTF-32

于 2009-01-30T17:10:09.520 回答
141
  • UTF-8 是可变的1 到 4个字节。

  • UTF-16 是可变的2 或 4个字节。

  • UTF-32 固定为4个字节。

注意:UTF-8 可以采用 1 到 6 个字节的最新约定:https ://lists.gnu.org/archive/html/help-flex/2005-01/msg00030.html

于 2009-01-30T17:10:29.830 回答
97

Unicode 定义了一个巨大的字符集,为每个图形符号分配一个唯一的整数值(这是一个主要的简化,实际上并不正确,但对于这个问题来说已经足够接近了)。UTF-8/16/32 只是不同的编码方式。

简而言之,UTF-32 对每个字符使用 32 位值。这允许他们为每个字符使用固定宽度的代码。

UTF-16 默认使用 16 位,但这只能为您提供 65k 个可能的字符,这对于完整的 Unicode 集来说还远远不够。所以有些字符使用成对的 16 位值。

而 UTF-8 默认使用 8 位值,这意味着前 127 个值是固定宽度的单字节字符(最高位用于表示这是一个多字节序列的开始,剩下 7实际字符值的位)。所有其他字符都被编码为最多 4 个字节的序列(如果有记忆的话)。

这使我们获得了优势。任何 ASCII 字符都直接与 UTF-8 兼容,因此对于升级旧版应用程序,UTF-8 是一个常见且显而易见的选择。在几乎所有情况下,它也会使用最少的内存。另一方面,您不能对字符的宽度做出任何保证。它可能是 1、2、3 或 4 个字符宽,这使得字符串操作变得困难。

UTF-32 则相反,它使用最多的内存(每个字符固定为 4 个字节宽),但另一方面,您知道每个字符都有这个精确的长度,因此字符串操作变得简单得多。您可以简单地根据字符串的字节长度计算字符串中的字符数。你不能用 UTF-8 做到这一点。

UTF-16 是一种妥协。它让大多数字符适合固定宽度的 16 位值。所以只要你没有中文符号、音符或其他,你可以假设每个字符都是16位宽。它使用的内存比 UTF-32 少。但它在某些方面是“两全其美”。它几乎总是比 UTF-8 使用更多的内存,并且仍然无法避免困扰 UTF-8(可变长度字符)的问题。

最后,使用平台支持的内容通常会有所帮助。Windows 内部使用 UTF-16,因此在 Windows 上,这是显而易见的选择。

Linux 略有不同,但它们通常对所有符合 Unicode 的内容使用 UTF-8。

如此简短的回答:所有三种编码都可以编码相同的字符集,但它们将每个字符表示为不同的字节序列。

于 2009-01-30T17:18:33.200 回答
52

Unicode是一种标准,关于UTF-x,您可以将其视为出于某些实际目的的技术实现:

  • UTF-8 - “大小优化”:最适合基于拉丁字符的数据(或 ASCII),每个字符只需要 1 个字节,但大小会相应增加符号种类(在最坏的情况下,每个字符可能增长到 6 个字节)
  • UTF-16 - “平衡”:每个字符至少需要 2 个字节,这对于现有的一组具有固定大小的主流语言来说足够了,以简化字符处理(但大小仍然是可变的,每个字符最多可以增长到 4 个字节)
  • UTF-32 - “性能”:允许使用简单算法作为固定大小字符(4 字节)的结果,但内存不足
于 2013-05-15T13:02:36.337 回答
41

我试图在我的博文中给出一个简单的解释

UTF-32

需要 32 位(4 个字节)来编码任何字符。例如,为了使用此方案表示“A”字符代码点,您需要将 65 写入 32 位二进制数:

00000000 00000000 00000000 01000001 (Big Endian)

如果您仔细观察,您会注意到最右边的七位在使用 ASCII 方案时实际上是相同的位。但是由于 UTF-32 是固定宽度方案,我们必须附加三个额外的字节。这意味着如果我们有两个只包含“A”字符的文件,一个是 ASCII 编码的,另一个是 UTF-32 编码的,它们的大小将分别为 1 字节和 4 字节。

UTF-16

很多人认为 UTF-32 使用固定宽度 32 位来表示代码点,因此 UTF-16 是固定宽度 16 位。错误的!

在 UTF-16 中,代码点可能以 16 位或 32 位表示。所以这个方案是变长编码系统。与 UTF-32 相比有什么优势?至少对于 ASCII,文件大小不会是原来的 4 倍(但仍然是两倍),所以我们仍然不能向后兼容 ASCII。

由于 7 位足以表示“A”字符,我们现在可以使用 2 个字节而不是 UTF-32 中的 4 个字节。它看起来像:

00000000 01000001

UTF-8

你猜对了。在 UTF-8 中,代码点可能使用 32、16、24 或 8 位来表示,并且作为 UTF-16 系统,这也是可变长度编码系统。

最后,我们可以像使用 ASCII 编码系统一样表示“A”:

01001101

UTF-16 实际上比 UTF-8 更好的一个小例子:

考虑中文字母“语” - 它的 UTF-8 编码是:

11101000 10101010 10011110

虽然它的 UTF-16 编码更短:

10001010 10011110

要了解表示形式及其解释方式,请访问原始帖子。

于 2016-02-03T16:16:07.583 回答
22

UTF-8

  • 没有字节顺序的概念
  • 每个字符使用 1 到 4 个字节
  • ASCII 是兼容的编码子集
  • 完全自同步,例如从流中任何地方丢弃的字节最多会损坏一个字符
  • 几乎所有欧洲语言都以每个字符两个字节或更少的字节编码

UTF-16

  • 必须使用已知的字节顺序或读取字节顺序标记 (BOM) 进行解析
  • 每个字符使用 2 或 4 个字节

UTF-32

  • 每个字符是 4 个字节
  • 必须使用已知的字节顺序或读取字节顺序标记 (BOM) 进行解析

除非大多数字符来自 CJK(中文、日文和韩文)字符空间,否则 UTF-8 将是最节省空间的。

UTF-32 最适合通过字符偏移到字节数组中进行随机访问。

于 2015-03-05T20:05:10.273 回答
15

在 UTF-32 中,所有字符都用 32 位编码。优点是可以轻松计算字符串的长度。缺点是对于每个 ASCII 字符,您会浪费额外的三个字节。

在 UTF-8 中字符长度可变,ASCII 字符编码为一个字节(八位),大多数西方特殊字符编码为两个字节或三个字节(例如 € 是三个字节),并且可以占用更多外来字符到四个字节。明显的缺点是,您无法先验地计算字符串的长度。但与 UTF-32 相比,对拉丁(英语)字母文本进行编码所需的字节数要少得多。

UTF-16 也是可变长度的。字符以两个字节或四个字节编码。我真的不明白这一点。它具有可变长度的缺点,但没有像 UTF-8 那样节省空间的优点。

在这三个中,显然 UTF-8 是传播最广泛的。

于 2009-01-30T17:24:45.153 回答
14

我做了一些测试来比较 MySQL 中 UTF-8 和 UTF-16 之间的数据库性能。

更新速度

UTF-8

在此处输入图像描述

UTF-16

在此处输入图像描述

插入速度

在此处输入图像描述

在此处输入图像描述

删除速度

在此处输入图像描述

在此处输入图像描述

于 2013-01-08T11:25:52.760 回答
7

我很惊讶这个问题已经 11 岁了,而且没有一个答案提到 utf-8 的 #1 优势。

utf-8 通常适用于不支持 utf-8 的程序。这部分是它的设计目的。其他答案提到前 128 个代码点与 ASCII 相同。所有其他代码点均由设置高位的 8 位值(值从 128 到 255)生成,因此从非 unicode 感知程序的 POV 中,它只会将字符串视为带有一些额外字符的 ASCII。

举个例子,假设您编写了一个程序来添加有效地执行此操作的行号(为了简单起见,我们假设行尾只是 ASCII 13)

// pseudo code

function readLine
  if end of file
     return null
  read bytes (8bit values) into string until you hit 13 or end or file
  return string

function main
  lineNo = 1
  do {
    s = readLine
    if (s == null) break;
    print lineNo++, s
  }  

将 utf-8 文件传递​​给该程序将继续工作。同样,拆分制表符、逗号、解析 ASCII 引号或其他只有 ASCII 值有意义的解析都只适用于 utf-8,因为 utf-8 中不会出现 ASCII 值,除非它们实际上是那些 ASCII 值

其他一些答案或评论提到 utf-32 的优点是您可以分别处理每个代码点。例如,这建议您可以采用“ABCDEFGHI”之类的字符串,并在每个第三个代码点将其拆分为

ABC
DEF
GHI

这是错误的。许多代码点会影响其他代码点。例如,让您在 ‍‍‍‍‍ 之间进行选择的颜色选择器代码点。如果您在任意代码点拆分,您将破坏这些代码点。

另一个例子是双向代码点。以下段落没有倒着输入。它前面只是 0x202E 代码点

  • 此行未向后键入,仅向后显示

所以不,utf-32 不会让你随意操作 unicode 字符串而不考虑它们的含义。它将让您无需额外代码即可查看每个代码点。

仅供参考,utf-8 的设计是为了查看任何单个字节,您可以找到当前代码点或下一个代码点的开始。

如果您在 utf-8 数据中取任意字节。如果它 < 128 它本身就是正确的代码点。如果它 >= 128 并且 < 192(前 2 位是 10),那么要找到代码点的开始,您需要查看前面的字节,直到找到值 >= 192 的字节(前 2 位是 11 )。在该字节处,您已找到代码点的开头。该字节编码了多少后续字节构成代码点。

如果您想找到下一个代码点,只需扫描到字节 < 128 或 >= 192,这就是下一个代码点的开始。

字节数 第一个代码点 最后一个代码点 字节 1 字节 2 字节 3 字节 4
1 U+0000 U+007F 0xxxxxxx
2 U+0080 U+07FF 110xxxxx 10xxxxxx
3 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

xxxxxx代码点的位在哪里。连接字节中的 xxxx 位以获取代码点

于 2021-01-07T03:31:17.403 回答
6

根据您的开发环境,您甚至可能无法选择您的字符串数据类型将在内部使用什么编码。

但是对于存储和交换数据,如果您可以选择,我将始终使用 UTF-8。如果您主要有 ASCII 数据,这将为您提供最少量的数据传输,同时仍然能够对所有内容进行编码。优化最少的 I/O 是现代机器上的方法。

于 2009-01-30T17:22:19.387 回答
2

如前所述,区别主要在于基础变量的大小,在每种情况下都会变大以允许表示更多字符。

然而,字体、编码和东西都非常复杂(不必要?),所以需要一个大链接来填写更多细节:

http://www.cs.tut.fi/~jkorpela/chars.html#ascii

不要期望全部理解,但是如果您不想以后遇到问题,那么值得尽可能早地学习(或者只是让其他人为您解决问题)。

保罗。

于 2009-01-30T17:17:07.883 回答
0

通读答案后,UTF-32 需要一些爱。

C#:

Data1 = RandomNumberGenerator.GetBytes(500_000_000);

sw = Stopwatch.StartNew();
int l = Encoding.UTF8.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"UTF-8: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s}   Size - {l:###,###,###}");

sw = Stopwatch.StartNew();
l = Encoding.Unicode.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"Unicode: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s}   Size - {l:###,###,###}");

sw = Stopwatch.StartNew();
l = Encoding.UTF32.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"UTF-32: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s}   Size - {l:###,###,###}");

sw = Stopwatch.StartNew();
l = Encoding.ASCII.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"ASCII: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s}   Size - {l:###,###,###}");

UTF-8 -- 经过 9.939 秒 - 大小 473,752,800

Unicode -- 经过 0.853 秒 - 大小 250,000,000

UTF-32 - 经过 3.143 秒 - 大小 125,030,570

ASCII -- 经过 2.362 秒 - 大小 500,000,000

UTF-32 -- 麦克风丢弃

于 2022-02-06T04:21:53.267 回答
-2

简而言之,使用 UTF-16 或 UTF-32 的唯一原因是分别支持非英语和古文字。

我想知道为什么有人会选择使用非 UTF-8 编码,因为它显然对 Web/编程目的更有效。

一个常见的误解 - 后缀数字并不表示其能力。它们都支持完整的 Unicode,只是 UTF-8 可以处理单个字节的 ASCII,因此对 CPU 和互联网来说更高效/不易损坏。

一些不错的阅读:http ://www.personal.psu.edu/ejp10/blogs/gotunicode/2007/10/which_utf_do_i_use.html 和http://utf8everywhere.org

于 2015-05-21T15:12:32.467 回答