ChatGPT解决这个技术问题 Extra ChatGPT

UTF-8、UTF-16 和 UTF-32

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

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

如果您对 Unicode 的工作原理感兴趣,请观看此视频youtube.com/watch?v=MijmeoH9LT4
该视频侧重于 UTF-8,是的,它很好地解释了可变长度编码的工作原理,并且主要与仅读取或写入固定长度 ASCII 的计算机兼容。 Unicode 家伙在设计 UTF-8 编码时很聪明。
UTF-8 是大多数现代软件中保存文件的事实标准。更具体地说,它是 HTML 以及配置和翻译文件使用最广泛的编码(例如,Minecraft 不接受对其所有文本信息的任何其他编码)。 UTF-32 用于内部内存表示的速度很快,而 UTF-16 有点不推荐使用,由于历史原因,目前仅在 Win32 中使用(当 Windows 95 出现时,UTF-16 是固定长度的)
@VladislavToncharov UTF-16 从来都不是固定长度的编码。您将它与 UCS-2 混淆了。
@Kotauskas Javascript 仍然对几乎所有内容使用 UTF-16

H
Hong Ooi

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

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

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


UTF-32 的优势:您不需要将存储的数据解码为 32 位 Unicode 代码点,例如逐个字符处理。代码点已经在您的数组/向量/字符串中可用。
如果(天堂帮助你)你必须重新实现轮子,它也更容易解析。
好吧,UTF-8 在网络传输中具有优势 - 无需担心字节顺序,因为您一次传输一个字节(而不是 4 个)。
@richq 您不能在 UTF-32 中进行逐个字符的处理,因为代码点并不总是对应于一个字符。
UTF-32 的优势:与 utf-8 等价物相比,字符串操作可能更快
A
Adam Rosenfield

简而言之:

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:固定宽度编码。所有代码点占用四个字节。一个巨大的内存猪,但操作起来很快。很少用。

长篇大论:参见 Wikipedia:UTF-8UTF-16UTF-32


@spurrymoses:我指的是数据字节占用的空间量。 UTF-8 每个亚洲字符需要 3 个字节,而 UTF-16 每个亚洲字符只需要 2 个字节。这确实不是一个大问题,因为与存储在程序内存中的平均文本量相比,如今计算机拥有大量内存。
UTF-32 不再很少使用了……在 osx 和 linux 上 wchar_t 默认为 4 个字节。 gcc 有一个选项 -fshort-wchar 可以将大小减少到 2 个字节,但会破坏与 std 库的二进制兼容性。
@PandaWood ofcource UTF-8 可以编码任何字符!但是您是否将内存需求与 UTF-16 的内存需求进行了比较?你似乎没有抓住重点!
如果有人在所有编码格式(包括那些不能编码 Unicode 的格式)的上下文中说 UTF-8 “对亚洲文本不太好”,那他们当然是错的。但这不是上下文。内存要求的上下文来自这样一个事实,即问题(和答案)是比较 UTF-8、UTF-16 和 UTF-32,它们都将对亚洲文本进行编码,但使用不同数量的内存/存储。因此,它们的相对优势自然完全取决于内存需求。 “不太好”!=“不太好”。
@McGafter:当然有。如果您想要值得信赖,请直接前往 The Unicode Consortium 的马口。有关 UTF-* 编码的描述,请参见第 2.5 章。但是为了获得对编码的简单、高级的理解,我发现维基百科的文章是一个更容易理解的来源。
O
Om Sao

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


UTF8 实际上是 1 到 6 个字节。
@Urkle 在技术上是正确的,因为映射 UTF32/LE/BE 的全部范围包括 U-00200000 - U-7FFFFFFF,即使 Unicode v6.3 以 U-0010FFFF 结尾。以下是如何对 5 和 6 字节 utf8 进行编码/解码的详细说明:lists.gnu.org/archive/html/help-flex/2005-01/msg00030.html
用相关的参考部分及其来源支持这些?
@Urkle 不,UTF-8 不能是 5 或 6 个字节。 Unicode 代码点限制为 21 位,这将 UTF-8 限制为 4 个字节。 (您当然可以扩展 UTF-8 的原理来编码任意大整数,但它不会是 Unicode。)参见 RFC 3629。
引用维基百科: 2003 年 11 月,UTF-8 受到 RFC 3629 的限制,以匹配 UTF-16 字符编码的约束:明确禁止高低代理字符对应的代码点删除超过 3% 的三字节序列, 并以 U+10FFFF 结尾删除了超过 48% 的四字节序列以及所有五字节和六字节序列。
P
Peter Mortensen

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。

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


说 Unicode 为每个图形符号分配一个唯一的整数是不准确的。它将这样分配给每个代码点,但有些代码点是不可见的控制字符,有些图形符号需要多个代码点来表示。
@tchrist:是的,这是不准确的。问题是要准确解释 Unicode,您需要编写数千页。我希望了解基本概念以解释编码之间的区别
@jalf 大声笑,所以基本上要解释 Unicode,您必须编写 Unicode Core Specification
迄今为止最好的答案
请注意,UTF-32 的描述是不正确的。每个字符不是 4 个字节宽。每个代码点有 4 个字节宽,有些字符可能需要多个代码点。计算字符串长度不仅仅是字节数除以 4,您必须遍历整个字符串并解码每个代码点以解析这些簇。
r
rogerdpack

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

UTF-8 - “大小优化”:最适合基于拉丁字符的数据(或 ASCII),每个字符只需要 1 个字节,但大小会相应增加符号种类(在最坏的情况下,每个字符可能增长到 6 个字节)

UTF-16 - “平衡”:每个字符至少需要 2 个字节,这对于现有的具有固定大小的主流语言集来说足够了,以简化字符处理(但大小仍然是可变的,每个字符最多可以增长到 4 个字节)

UTF-32 - “性能”:允许使用简单算法作为固定大小字符(4 字节)的结果,但内存不足


«主流语言»在世界许多地方并不是主流^^
UTF-16 实际上针对非 ASCII 字符进行了大小优化。因为这真的取决于它将使用哪种语言。
@tuxayo 完全同意,值得注意的是亚洲部分的汉字和汉字字符集。
应该是最佳答案。这太正确了,不能被埋在这里。
utf-8 可能比所有这些都快,只是因为开发人员花费了最多的精力来优化它
M
Maroun

我试图在我的 blogpost 中给出一个简单的解释。

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

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


计算机如何不“丢弃”包含大量零的 UTF-32 编码数字?就像代表'A'将包含26-27个零......
C
Community

UTF-8

没有字节顺序的概念

每个字符使用 1 到 4 个字节

ASCII 是兼容的编码子集

完全自同步,例如从流中任何地方丢弃的字节最多会损坏一个字符

几乎所有欧洲语言都以每个字符两个字节或更少的字节编码

UTF-16

必须使用已知的字节顺序或读取字节顺序标记 (BOM) 进行解析

每个字符使用 2 或 4 个字节

UTF-32

每个字符是 4 个字节

必须使用已知的字节顺序或读取字节顺序标记 (BOM) 进行解析

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

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


“自同步”如何在 UTF-8 中工作?你能举出 1 字节和 2 字节字符的例子吗?
@KorayTugay 有效的较短字节字符串永远不会用于较长的字符。例如,ASCII 在 0-127 范围内,这意味着所有单字节字符在二进制中都具有 0xxxxxxx 形式。所有双字节字符均以 110xxxxx 开头,第二个字节为 10xxxxxx。所以假设一个两字节字符的第一个字符丢失了。只要您看到 10xxxxxx 而没有前面的 110xxxxxx,您就可以确定一个字节丢失或损坏,并丢弃该字符(或从服务器重新请求它或其他),然后继续前进,直到您再次看到一个有效的第一个字节。
如果你有一个字符的偏移量,你就有那个字符的偏移量——在这种情况下,utf8、utf16 或 utf32 的工作方式相同;即它们都同样擅长通过字符偏移随机访问字节数组。认为 utf32 比 utf8 更擅长计算字符的想法也是完全错误的。一个代码点(与字符不同,又与字形不同......叹息),在 utf32 中是 32 位宽,在 utf8 中是 8 到 32 位之间,但是一个字符可能跨越多个代码点,这破坏了人们声称 utf32 优于 utf8 的主要优势。
@Clearer但是您需要多久使用一次字符/字素而不仅仅是代码点?我从事过许多涉及繁重的字符串操作的项目,能够在 O(1) 中对代码点进行切片/索引确实非常有帮助。
@RedwolfPrograms 今天我没有,但我曾经从事语言分析工作,这非常重要。
C
Community

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

更新速度

UTF-8

https://i.stack.imgur.com/AK6CK.png

UTF-16

https://i.stack.imgur.com/FwSHZ.png

插入速度

https://i.stack.imgur.com/3HYaA.png

https://i.stack.imgur.com/3cUGV.png

删除速度

https://i.stack.imgur.com/3QcMo.png

https://i.stack.imgur.com/corSh.png


一个短字符串没有任何意义,一个记录就更少了,时间上的差异可能是其他因素造成的,Mysql自己的内部机制,如果你想做一个可靠的测试,你至少需要使用10,000条记录使用 200 个字符的字符串,并且需要一组测试,在某些情况下,至少大约 3 个,因此它将隔离编码因子
A
Ahmad F

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

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

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

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


为什么我要在开发网站时计算字符串的长度?在 Web 开发中选择 UTF-8/UTF-16 有什么好处吗?
“优点是您可以轻松计算字符串的长度”如果您通过# of codepoints定义长度,那么是的,您只需将字节长度除以4即可使用UTF-32得到它。然而,这不是一个非常有用的定义:它可能与字符数无关。此外,规范化可能会改变字符串中代码点的数量。例如,法语单词“été”可以用至少 4 种不同的方式编码,具有 3 个不同的代码点长度。
UTF-16 可能比 UTF-8 更快,同时也不会像 UTF-32 那样浪费内存。
@MichalŠtein 但它也给了你两全其美;对于 ASCII,它比 UTF-8 占用更多的空间,但它也存在由于每个字符有多个代码点而导致的所有相同问题(除了潜在的字节顺序问题)。
s
samanthaj

我很惊讶这个问题已经 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

这是错误的。 Many code points affect other code points。例如,颜色选择器代码点可让您在👨🏻‍🦳👨🏼‍🦳👨🏽‍🦳👨🏾‍🦳👨🏿‍🦳之间进行选择。如果您在任意代码点拆分,您将破坏这些代码点。

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

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

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

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

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

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

Num Bytes 第一个代码点 最后一个代码点 Byte 1 Byte 2 Byte 3 Byte 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 位以获取代码点


P
Peter Mortensen

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

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


可以说,比空间要求更重要的是 UTF-8 不受字节顺序的影响。 UTF-16 和 UTF-32 将不可避免地要处理字节顺序问题,其中 UTF-8 只是一个八位字节流。
P
Paul W Homer

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

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

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

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

保罗。


或者只是使用 UTF-8 作为默认值,因为它已成为事实上的标准,并找出新系统是否支持它。如果没有,你可以回到这个帖子。
@paul-w-homer 您的链接已损坏。
K
Keith Downes

通读答案后,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 -- 麦克风丢弃


k
killjoy

简而言之,使用 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.htmlhttp://utf8everywhere.org


我不确定您为什么建议使用 UTF-16 或 UTF-32 来支持非英文文本。 UTF-8 可以处理得很好。英文文本中也有非 ASCII 字符。就像一个零宽度的非连接器。或者一个破折号。恐怕,这个答案并没有增加太多价值。
这个问题很容易被否决,因为 UTF-8 在 HTML 文件中仍然很常用,即使大多数字符是 UTF-8 中的 3 字节字符,
@IInspectable 支持不是最好的措辞,促进或更好的支持会更准确
发送像 utf8everywhere.org 这样的页面不是我在 SO 答案中会做的事情。