什么是 Unicode、UTF-8 和 UTF-16?

What are Unicode, UTF-8, and UTF-16?

提问人:SoftwareGeek 提问时间:2/11/2010 最后编辑:Peter MortensenSoftwareGeek 更新时间:2/21/2022 访问量:366879

问:

Unicode 的基础是什么,为什么需要 UTF-8 或 UTF-16? 我已经在谷歌上研究过这个,也在这里搜索过,但我不清楚。

VSS 中,在进行文件比较时,有时会出现一条消息,指出两个文件具有不同的 UTF。为什么会这样?

请用简单的术语解释。

Unicode 编码 UTF-16

评论

149赞 Brian Agnew 2/11/2010
听起来你需要阅读 The Absolute Minimum Every Software Developer Absolutely, Positive Must Know About Unicode and Character Sets!这是对正在发生的事情的一个很好的解释。
4赞 Joachim Sauer 6/24/2011
@John:这是一个非常好的介绍,但它不是最终的来源:它跳过了很多细节(这对于概述/介绍来说很好!
6赞 Pavel Radzivilovsky 5/27/2012
这篇文章很棒,但它有几个错误,并且以某种保守的方式代表了 UTF-8。我建议阅读 utf8everywhere.org 作为补充。
5赞 Nemanja Trifunovic 2/12/2010
Unicode官方网站上的这个FAQ为您提供了一些答案。
2赞 Vertexwahn 1/14/2016
看看这个网站: utf8everywhere.org

答:

22赞 dan04 7/5/2010 #1

最初,Unicode 旨在具有固定宽度的 16 位编码 (UCS-2)。Unicode 的早期采用者,如 Java 和 Windows NT,围绕 16 位字符串构建了他们的库。

后来,Unicode 的范围扩大到包括历史字符,这需要超过 65,536 位编码支持的 16 个代码点。为了允许在使用 UCS-2 的平台上表示其他字符,引入了 UTF-16 编码。它使用“代理项对”来表示补充平面中的字符。

同时,许多较旧的软件和网络协议都使用 8 位字符串。UTF-8 的制作是为了让这些系统可以支持 Unicode,而不必使用宽字符。它向后兼容 7 位 ASCII。

评论

4赞 Mark Ransom 1/13/2017
值得注意的是,Microsoft仍然将UTF-16称为Unicode,这增加了混乱。两者是不一样的。
673赞 DPenner1 2/28/2013 #2

为什么我们需要Unicode?

在(不太)早期,所有存在的都是 ASCII。这没关系,因为只需要一些控制字符、标点符号、数字和字母,就像这句话中的那些一样。不幸的是,今天全球互通和社交媒体的奇怪世界是没有预见到的,在同一文档中看到英语、العربية、汉语、עִבְרִית、ελληνικά 和 ភាសាខ្មែរ 并不罕见(我希望我没有破坏任何旧浏览器)。

但为了论证,假设 Joe Average 是一名软件开发人员。他坚持认为他只需要英语,因此只想使用ASCII。这对用户 Joe 来说可能没问题,但对软件开发人员 Joe 来说就不行了。世界上大约有一半的人使用非拉丁字符,使用ASCII可以说是对这些人的不体贴,最重要的是,他正在将他的软件与一个庞大且不断增长的经济体关闭。

因此,需要一个包含所有语言的字符集。于是出现了Unicode。它为每个字符分配一个唯一的编号,称为代码点。与其他可能的集合相比,Unicode 的一个优点是前 256 个码位与 ISO-8859-1 相同,因此也与 ASCII 相同。此外,绝大多数常用字符只能用两个字节表示,在称为基本多语言平面 (BMP) 的区域中。现在需要字符编码才能访问此字符集,正如问题所问的,我将专注于 UTF-8 和 UTF-16。

内存注意事项

那么,有多少字节可以访问这些编码中的哪些字符呢?

  • UTF-8:
  • 1 字节:标准 ASCII
  • 2 字节:阿拉伯语、希伯来语、大多数欧洲文字(最明显的不包括格鲁吉亚语)
  • 3 字节:BMP
  • 4 字节:所有 Unicode 字符
  • UTF-16:
  • 2 字节:BMP
  • 4 字节:所有 Unicode 字符

值得一提的是,BMP中没有的字符包括古代文字、数学符号、音乐符号以及更稀有的中文、日文和韩文(CJK)字符。

如果您主要使用 ASCII 字符,那么 UTF-8 肯定更节省内存。但是,如果您主要使用非欧洲脚本,则使用 UTF-8 的内存效率可能比 UTF-16 低 1.5 倍。在处理大量文本(如大型网页或冗长的 Word 文档)时,这可能会影响性能。

编码基础知识

注意:如果您知道 UTF-8 和 UTF-16 是如何编码的,请跳到下一节了解实际应用。

  • UTF-8:对于标准 ASCII (0-127) 字符,UTF-8 代码是相同的。如果需要与现有 ASCII 文本向后兼容,则 UTF-8 是理想的选择。其他字符需要 2-4 个字节。这是通过在每个字节中保留一些位来完成的,以指示它是多字节字符的一部分。特别是,每个字节的第一位是为了避免与 ASCII 字符冲突。1
  • UTF-16:对于有效的 BMP 字符,UTF-16 表示形式只是其代码点。但是,对于非 BMP 字符,UTF-16 引入了代理项对。在本例中,两个双字节部分的组合映射到非 BMP 字符。这些双字节部分来自 BMP 数字范围,但由 Unicode 标准保证作为 BMP 字符无效。此外,由于 UTF-16 有两个字节作为其基本单位,因此它受到字节序的影响。作为补偿,可以在数据流的开头放置一个保留的字节顺序标记,以指示字节序。因此,如果要读取 UTF-16 输入,并且未指定字节序,则必须检查这一点。

可以看出,UTF-8 和 UTF-16 彼此之间相去甚远。因此,如果您正在执行 I/O,请确保您知道您使用的是哪种编码!有关这些编码的更多详细信息,请参阅 UTF 常见问题解答

实际编程注意事项

字符和字符串数据类型:它们是如何用编程语言编码的?如果它们是原始字节,则在尝试输出非 ASCII 字符的那一刻,可能会遇到一些问题。此外,即使字符类型基于 UTF,也不意味着字符串是正确的 UTF。它们可能允许非法的字节序列。通常,您必须使用支持 UTF 的库,例如用于 C、C++ 和 Java 的 ICU。无论如何,如果要输入/输出默认编码以外的内容,则必须先对其进行转换。

推荐编码、默认编码和主要编码:当可以选择使用哪种 UTF 时,通常最好遵循您所处环境的推荐标准。例如,UTF-8 在 Web 上占主导地位,自 HTML5 以来,它一直是推荐的编码。相反,.NETJava 环境都建立在 UTF-16 字符类型之上。令人困惑(和错误地)的是,经常引用“Unicode 编码”,它通常是指给定环境中的主要 UTF 编码。

图书馆支持:您正在使用的库支持某种编码。哪一个?他们支持极端情况吗?由于必要性是发明之母,UTF-8 库通常会正确支持 4 字节字符,因为 1、2 甚至 3 字节字符可能会频繁出现。但是,并非所有所谓的 UTF-16 库都正确支持代理项对,因为它们很少出现。

计数字符:Unicode 中存在组合字符。例如,代码点 U+006E (n) 和 U+0303(组合波浪号)形成 ñ,但代码点 U+00F1 形成 ñ。它们应该看起来相同,但一个简单的计数算法将为第一个示例返回 2,对于后一个示例返回 1。这不一定是错的,但也可能不是预期的结果。

比较平等:A、А 和 Α 看起来相同,但它们分别是拉丁语、西里尔语和希腊语。您也有 C 和 C 等情况。一个是字母,另一个是罗马数字。此外,我们还需要考虑组合角色。有关详细信息,请参阅 Unicode 中的重复字符

代理对:这些在 Stack Overflow 上经常出现,所以我只提供一些示例链接:

评论

11赞 Joachim Sauer 2/28/2013
很好的答案,赏金的机会很大;-)就我个人而言,我想补充一点,有些人认为 UTF-8 是通用字符编码,但我知道这种观点不一定得到所有人的认同。
3赞 FirstName LastName 5/13/2013
在这个阶段对我来说仍然太技术化了。hello 这个词是如何以 UTF-8 和 UTF-16 格式存储在计算机中的?
1赞 mark 10/8/2014
您能否详细解释一下为什么 BMP 在 UTF-8 中需要 3 个字节?我本来以为,由于它的最大值是 0xFFFF(16 位),那么它只需要 2 个字节即可访问。
2赞 DPenner1 10/8/2014
@mark 某些位保留用于编码目的。对于以 UTF-8 格式占用 2 个字节的码位,有 5 个保留位,只剩下 11 位来选择一个码位。U+07FF 最终成为以 2 个字节表示的最高码位。
1赞 Tuxdude 2/16/2016
顺便说一句 - ASCII 仅定义 128 个码位,仅使用 7 位表示。它是 ISO-8859-1/ISO-8859-15,它定义了 256 个码位并使用 8 位表示。这 3 个中的前 128 个代码点是相同的。
10赞 brighty 1/15/2014 #3

为什么选择Unicode?因为 ASCII 只有 127 个字符。从 128 到 255 的那些在不同的国家/地区有所不同,这就是有代码页的原因。所以他们说:让我们最多有1114111个角色。

那么如何存储最高码位呢?您需要使用 21 位来存储它,因此您将使用具有 32 位的 DWORD,其中 11 位被浪费。因此,如果使用 DWORD 来存储 Unicode 字符,这是最简单的方法,因为 DWORD 中的值与代码点完全匹配。

但是DWORD数组当然比WORD数组大,当然甚至比BYTE数组还要大。这就是为什么不仅有 UTF-32,还有 UTF-16。但是 UTF-16 表示 WORD 流,而 WORD 有 16 位,那么最高的代码点如何1114111适合 WORD?它不能!

因此,他们将所有高于 65535 的东西都放入一个 DWORD 中,他们称之为代理对。这样的代理项对是两个 WORD,可以通过查看前 6 位来检测。

那么 UTF-8 呢?它是一个字节数组或字节流,但最高的代码点1114111如何适应一个字节呢?它不能!好的,所以他们也放了一个DWORD,对吧?或者可能是一个词,对吧?差不多了!

他们发明了 utf-8 序列,这意味着每个高于 127 的码位都必须编码为 2 字节、3 字节或 4 字节序列。哇!但是我们如何检测这样的序列呢?好吧,127 以内的所有内容都是 ASCII 并且是一个字节。以 110 开头的是双字节序列,以 1110 开头的是三字节序列,以 11110 开头的是四字节序列。这些所谓的“startbytes”的剩余位属于代码点。

现在,根据顺序,必须遵循以下字节。后面的字节以 10 开头,其余位是 6 位有效负载位,属于代码点。将起始字节和后续字节的有效负载位连接起来,您将获得代码点。这就是 UTF-8 的全部魔力。

评论

5赞 brighty 1/15/2014
utf-8 以 utf-8 3 字节序列解码的 €(欧元)符号示例:E2=11100010 82=10000010 AC=10101100 如您所见,E2 以 1110 开头,所以这是一个三字节序列 如您所见,82 和 AC 以 10 开头,所以这些是以下字节 现在我们将“有效负载位”连接起来: 0010 + 000010 + 101100 = 10000010101100 十进制 8364 所以 8364 必须是 €(欧元)符号的代码点。
95赞 wengeezhang 1/6/2015 #4
  • Unicode的
    • 是世界各地使用的一组字符
  • UTF-8 格式
    • 一种字符编码,能够对 Unicode 中所有可能的字符(称为代码点)进行编码。
    • 代码单元为 8 位
    • 使用 1 到 4 个代码单元对 Unicode 进行编码
    • 00100100表示“$”(一个 8 位);11000010 10100010表示“¢”(两个 8 位);”的11100010 10000010 10101100(三个 8 位)
  • UTF-16 格式
    • 另一个字符编码
    • 代码单元为 16 位
    • 使用一到两个代码单元对 Unicode 进行编码
    • 00000000 00100100表示“$”(一个 16 位);11011000 “𤭢”的01010010 11011111 01100010(两个 16 位)

评论

0赞 Peter Mortensen 2/21/2022
“两个 16 位”之前的字符不呈现(Ubuntu MATE 20.04 (Focal Fossa) 上的 Firefox 版本 97.0)。
35赞 Neuron 10/27/2015 #5

Unicode是一个相当复杂的标准。不要太害怕,但要 为一些工作做好准备![2]

因为总是需要可靠的资源,但官方报告是巨大的,我建议阅读以下内容:

  1. 绝对最低限度 每个软件开发人员绝对、肯定地必须了解 Unicode 和字符集(没有任何借口!Stack Exchange 首席执行官 Joel Spolsky 的介绍。
  2. 到 BMP 及以后!Eric Muller(当时的技术总监,后来是Unicode联盟的副总裁)的教程(前20张幻灯片,你就完成了)

简要说明:

计算机读取字节,人读取字符,因此我们使用编码标准将字符映射到字节。ASCII 是第一个广泛使用的标准,但仅涵盖拉丁语(7 位/字符可以代表 128 个不同的字符)。Unicode 是一种标准,其目标是涵盖世界上所有可能的字符(最多可容纳 1,114,112 个字符,这意味着每个字符最多 21 位。当前的 Unicode 8.0 总共指定了 120,737 个字符,仅此而已)。

主要区别在于 ASCII 字符可以适合一个字节(八位),但大多数 Unicode 字符不能。因此,使用编码形式/方案(如 UTF-8 和 UTF-16),字符模型如下:

每个字符都有一个从 0 到 1,114,111(十六进制:0-10FFFF)的枚举位置,称为代码点
编码形式将代码点映射到代码单元序列。代码单元是您希望字符在内存、8 位单位、16 位单位等中的组织方式。UTF-8 使用 1 到 4 个 8 位单位,UTF-16 使用 1 个或两个 16 位单位,以覆盖最多 21 位的整个 Unicode。单位使用前缀,以便可以发现字符边界,单位越多意味着占用位的前缀越多。因此,尽管 UTF-8 对拉丁脚本使用一个字节,但它需要三个字节用于基本多语言平面内的后续脚本,而 UTF-16 对所有这些脚本使用两个字节。这就是他们的主要区别。
最后,编码方案(如 UTF-16BE 或 UTF-16LE)将代码单元序列映射(序列化)到字节序列。

字符: π
码位: U+03C0 编码形式(代码单位): UTF-8: CF 80 UTF-16: 03C0
编码方案(字节):

UTF-8: CF 80 UTF-16BE: 03 C0


UTF-16LE: C0
03

提示:十六进制数字表示四个位,因此两位十六进制数字表示一个字节。
还可以查看维基百科上的平面图,以了解字符集布局。

评论

0赞 Peter Mortensen 2/21/2022
乔尔·斯波尔斯基(Joel Spolsky)不再是首席执行官。
3赞 Krishna Ganeriwal 8/30/2016 #6

UTF 代表 Unicode 转换格式。基本上,在当今世界,有数百种其他语言编写的脚本,这些格式是之前使用的基本 ASCII 所未涵盖的。因此,UTF应运而生。

UTF-8 具有字符编码功能,其代码单元为 8 位,而 UTF-16 的代码单元为 16 位。

33赞 InGeek 1/18/2017 #7

每个程序员绝对需要了解的关于编码和字符集以处理文本的文章解释了所有细节。

写入缓冲区

如果写入 4 字节缓冲区,则使用 UTF8 编码的符号,则二进制文件将如下所示:

00000000 11100011 10000001 10000010

如果写入 4 字节缓冲区,则使用 UTF16 编码的符号,则二进制文件将如下所示:

00000000 00000000 00110000 01000010

正如你所看到的,根据你在内容中使用的语言,这将相应地影响你的记忆。

示例:对于此特定符号:UTF16 编码效率更高,因为我们有 2 个备用字节可用于下一个符号。但这并不意味着您必须使用 UTF16 作为日本字母。

从缓冲区读取

现在,如果你想读取上面的字节,你必须知道它是用什么编码写的,并正确地解码回来。

例如,如果您将此 : 00000000 11100011 10000001 10000010解码为 UTF16 编码,您最终将得到

注意:编码和 Unicode 是两回事。Unicode 是一个大(表),每个符号都映射到一个唯一的码位。例如: 符号(字母)有一个(码位):30 42(十六进制)。另一方面,编码是一种在存储到硬件时将符号转换为更合适的方式的算法。

30 42 (hex) - > UTF8 encoding - > E3 81 82 (hex), which is above result in binary.

30 42 (hex) - > UTF16 encoding - > 30 42 (hex), which is above result in binary.

Enter image description here

评论

0赞 bomben 9/14/2021
很好的答案,我投了赞成票。你能不能好心地检查一下你的答案的这一部分是否是你认为它应该的样子(因为它没有意义):“将符号转换为更合适的方式”。
1赞 Peter Mortensen 2/21/2022
参考文献的标题,“每个程序员绝对、积极地需要了解的关于编码和字符集以处理文本的知识”,几乎是对乔尔·斯波尔斯基(Joel Spolsky)的每个软件开发人员绝对、积极地必须了解Unicode和字符集的绝对最低限度(没有借口!)”的剽窃。
13赞 Kishu Agarwal 3/25/2017 #8

Unicode 是一种标准,它将所有语言中的字符映射到称为码位的特定数值。它这样做的原因是它允许使用相同的代码点集进行不同的编码。

UTF-8 和 UTF-16 是两种这样的编码。它们将代码点作为输入,并使用一些定义良好的公式对它们进行编码以生成编码的字符串。

选择特定编码取决于您的要求。不同的编码具有不同的内存要求,并且根据您将要处理的字符,您应该选择使用最少的字节序列来编码这些字符的编码。

有关 Unicode、UTF-8 和 UTF-16 的更深入的详细信息,您可以查看这篇文章,

每个程序员都应该了解的关于Unicode的知识

8赞 Siva 12/6/2018 #9

ASCII - 软件在内存中仅为给定字符分配 8 位字节。它适用于英语和采用(外来词,如 facade)字符,因为它们对应的十进制值低于 128。示例 C 程序。

UTF-8 - 软件为给定字符分配一到四个可变的 8 位字节。这里的变量是什么意思?假设您正在通过浏览器中的 HTML 页面发送字符“A”(HTML 是 UTF-8),A 对应的十进制值为 65,当您将其转换为十进制时,它变为 01000010。这只需要一个字节,即使对于特殊采用的英文字符(如单词 facade 中的“ç”)也会分配一个字节内存。但是,当您要存储欧洲字符时,它需要两个字节,因此您需要 UTF-8。但是,当您选择亚洲字符时,您至少需要两个字节,最多需要四个字节。同样,表情符号需要三到四个字节。UTF-8 将解决您的所有需求。

UTF-16 将分配每个字符最少 2 个字节,最多 4 个字节,它不会分配 1 或 3 个字节。每个字符都以 16 位或 32 位表示。

那么为什么UTF-16存在呢?最初,Unicode 是 16 位而不是 8 位。Java 采用了 UTF-16 的原始版本。

简而言之,除非它已被您正在使用的语言或平台采用,否则您在任何地方都不需要 UTF-16。

Web 浏览器调用的 Java 程序使用 UTF-16,但 Web 浏览器使用 UTF-8 发送字符。

评论

0赞 Tom Blodget 12/8/2018
“除非它已经被语言或平台采用,否则你不需要 UTF-16”:这是一个很好的观点,但这里有一个非包容性列表:JavaScript、Java、.NET、SQL NCHAR、SQL NVARCHAR、VB4、VB5、VB6、VBA、VBScript、NTFS、Windows API......
0赞 Peter Mortensen 2/21/2022
回复“当你想存储欧洲字符时,它需要两个字节,所以你需要 UTF-8”:除非使用代码页,例如 CP-1252
0赞 Peter Mortensen 2/21/2022
回复“Web 浏览器使用 UTF-8 发送字符”:除非在网页上指定了 ISO 8859-1 之类的内容(?例如<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">