什么是Unicode,UTF-8,UTF-16?


395

Unicode的基础是什么,为什么需要UTF-8或UTF-16?我已经在Google上对此进行了研究,并且也在此处进行了搜索,但我不清楚。

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

请简单说明。


123
听起来您需要阅读《绝对最低知识》,每个软件开发人员绝对,肯定必须了解Unicode和字符集!这是对发生的事情的很好的解释。
Brian Agnew

5
Unicode官方网站上的常见问题解答为您提供了一些答案。
Nemanja Trifunovic

4
@John:这是一个非常不错的介绍,但这不是最终的出处:它跳过了很多细节(对于概述/介绍来说很好!)
Joachim Sauer

5
这篇文章很棒,但是有一些错误,以保守的眼光代表了UTF-8。我建议阅读utf8everywhere.org作为补充。
帕维尔·拉兹维洛夫斯基

2
看看这个网站:utf8everywhere.org
Vertexwahn,2016年

Answers:


550

为什么我们需要Unicode?

在早期(不是很早),仅存在ASCII。没关系,因为只需要一些控制字符,标点符号,数字和字母即可。不幸的是,没有预见到当今全球交流和社交媒体的陌生世界,并且在同一文档中看到英语,العربية,汉语,,בְרִית,ελληνικά和ភាសាខ្មែរ并不少见(我希望我不会打破任何旧的浏览器)。

但是,为了争辩,可以说乔·阿维尔是一名软件开发人员。他坚持认为,他只会需要英语,因此只想使用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个字节。这是通过在每个字节中保留一些位来表示它是多字节字符的一部分来完成的。特别是,每个字节的第一位是1为了避免与ASCII字符冲突。
  • UTF-16:对于有效的BMP字符,UTF-16表示只是其代码点。但是,对于非BMP字符,UTF-16引入了代理对。在这种情况下,两个两个字节的部分的组合映射到一个非BMP字符。这两个字节的部分来自BMP数值范围,但是Unicode标准保证它们作为BMP字符无效。另外,由于UTF-16具有两个字节作为其基本单位,因此受endianness的影响。为了补偿,可以在表示字节序的数据流的开头放置一个保留的字节顺序标记。因此,如果您正在读取UTF-16输入,并且未指定字节序,则必须进行检查。

可以看出,UTF-8和UTF-16彼此之间几乎没有兼容性。因此,如果要执行I / O,请确保您知道所使用的编码!有关这些编码的更多详细信息,请参阅UTF常见问题解答

实际编程注意事项

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

推荐/默认/主要编码:选择使用哪种UTF时,通常最好遵循所使用环境的推荐标准。例如,UTF-8在Web上占主导地位,而自HTML5起,一直是推荐的编码。相反,.NET和Java环境都基于UTF-16字符类型。令人困惑(且错误地)的是,通常会引用“ Unicode编码”,这通常是指给定环境中占主导地位的UTF编码。

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

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

比较是否相等: A,А和Α外观相同,但分别为拉丁文,西里尔文和希腊文。您也有C和like这样的情况,一个是字母,另一个是罗马数字。另外,我们还要考虑组合字符。有关更多信息,请参见Unicode中的重复字符

代理对:这些通常在SO上经常出现,因此我仅提供一些示例链接:

其他?:


11
很好的答案,赏金的机会很大;-)我个人会补充说,有人认为UTF-8是通用字符编码,但是我知道那是一种观点,不一定每个人都认同
约阿希姆·绍尔

3
在这个阶段对我来说仍然太技术性。hello一词如何存储在UTF-8和UTF-16的计算机中?
FirstName LastName

1
您能否进一步说明为什么BMP在UTF-8中占用3个字节?我以为,由于其最大值是0xFFFF(16位),因此只需要2个字节即可访问。
2014年

2
@mark某些位保留用于编码。对于在UTF-8中占用2个字节的代码点,有5个保留位,仅剩下11位来选择代码点。U + 07FF最终成为2个字节可表示的最高代码点。
DPenner1 2014年

1
BTW-ASCII仅定义128个代码点,仅使用7位表示。ISO-8859-1 / ISO-8859-15定义了256个代码点,并使用8位表示。所有这3个中的前128个代码点都相同。
Tuxdude '16

67
  • 统一码
    • 是全世界使用的一组字符
  • UTF-8
    • 能够对Unicode中所有可能的字符(称为代码点)进行编码的字符编码。
    • 代码单位是8位
    • 使用一到四个代码单元编码Unicode
    • 00100100为“ $ ”(一个8位);11000010 10100010表示“ ¢ ”(两个8位);11100010 10000010 10101100表示“ ”(三个8位)
  • UTF-16
    • 另一个字符编码
    • 代码单位是16位
    • 使用一到两个代码单元编码Unicode
    • 00000000 00100100 for“ $ ”(一个16位);11011000 01010010 11011111 01100010为“ 𤭢 ”(2个16位)

1
简短而准确
Aritra Chatterjee

30

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

由于始终需要可靠的资源,但是官方报告非常庞大,因此建议阅读以下内容:

  1. 每个软件开发人员绝对,肯定必须绝对了解Unicode和字符集(无借口!) Stack Exchange首席执行官Joel Spolsky的介绍。
  2. 到BMP之外!Unicode联盟的技术总监,后来的副总裁Eric Muller编写了一个教程。(前20张幻灯片,您就完成了)

简要说明:

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

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

每个字符都有一个从0到1,114,111(十六进制:0-10FFFF)的枚举位置,称为代码点
一个编码形式映射的码点到编码单元序列。一个代码单元是你想在内存中,8位,16位为单位来组织文字等方式。UTF-8使用1到4个8位单位,而UTF-16使用1或2个16位单位,以覆盖最大21位的整个Unicode。单位使用前缀,以便可以发现字符边界,而更多的单位意味着更多的占用位的前缀。因此,尽管UTF-8在拉丁文脚本中使用了1个字节,但在基本多语言平面内的后续脚本中则需要3个字节,而UTF-16在所有这些语言中都使用了2个字节。这就是他们的主要区别。
最后,编码方案 (例如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

提示:十六进制数字代表4位,所以两位数字十六进制数字代表一个字节
还可以查看Wikipedia中的Plane maps,以了解字符集的布局


19

最初,Unicode被设计为具有固定宽度的16位编码(UCS-2)。Unicode的早期采用者(例如Java和Windows NT)围绕16位字符串构建其库。

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

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


3
值得注意的是,Microsoft 仍将 UTF-16称为Unicode,这增加了混乱。两者一样。
Mark Ransom

15

本文介绍了所有详细信息 http://kunststube.net/encoding/

写缓冲区

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

00000000 11100011 10000001 10000010

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

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.

在此处输入图片说明


11

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

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

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

有关Unicode,UTF-8和UTF-16的更多详细信息,请查看本文,

每个程序员都应该了解Unicode


9

为什么要使用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开头的是四个字节的序列。这些所谓的“起始字节”的其余位属于代码点。现在,根据顺序,必须跟随以下字节。接下来的字节以10开头,其余位是有效载荷位的6位,属于代码点。将起始字节的有效负载位与随后的字节/秒连接起来,您将获得代码点。这就是utf-8的全部魔力。


3
utf-8以utf-8 3字节序列解码的欧元符号的示例:E2 = 11100010 82 = 10000010 AC = 10101100如您所见,E2以1110开头,所以这是一个三字节序列如您所见,82以及AC均以10开头,因此以下是字节。现在,我们将“有效载荷位”连接起来:0010 + 000010 + 101100 = 10000010101100,十进制为8364,因此8364必须是€(欧元)符号的代码点。
Brighty 2014年

5

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

UTF-8-软件为给定字符分配1到4个可变8位字节。这里的变量是什么意思?假设您正在通过浏览器中的HTML页面发送字符“ A”(HTML为UTF-8),则A的对应十进制值为65,将其转换为十进制时它将变为01000010。这仅需要1个字节,甚至会为façade中的“ç”等特殊采用的英语字符分配1个字节的内存。但是,当您要存储欧洲字符时,它需要2个字节,因此您需要UTF-8。但是,当您使用亚洲字符时,需要最少2个字节,最多4个字节。同样,表情符号需要3到4个字节。UTF-8将解决您的所有需求。

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

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

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

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


“除非语言或平台已经采用了UTF-16,否则您在任何地方都不需要UTF-16”:这是一个不错的主意,但这是一个不包含在内的列表:JavaScript,Java,.NET,SQL NCHAR,SQL NVARCHAR ,VB4,VB5,VB6,VBA,VBScript,NTFS,Windows API…。
汤姆·布洛杰特

2

UTF代表Unicode转换格式。在当今世界,基本上有以数百种其他语言编写的脚本,这些格式未被早期使用的基本ASCII覆盖。因此,UTF诞生了。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.