UTF-8和Unicode有什么区别?


503

根据Wikipedia UTF-8页面,我听到人们的意见不一致。

他们是同一回事,不是吗?有人可以澄清吗?


1
我认为这本WIKI关于unicode和UTF的内容是可以的。关于它的一些评论很奇怪:“在UTF-8(或任何其他多字节编码)中,可能会在字符中间拆分或截断字符串,这可能会导致字符串无效。” 因此,经过UTF-8编码的字符串不再是字符串,而是字节数组或字节流。组成字符串的字符得到编码。当然,它也可以被解码。当然,现在您可以在起始字节之后或后一个字节之后剪切utf-8序列,但是为什么有人要这样做呢?
Brighty 2014年

这篇有关字符串数据类型的文章具有教育意义:mortoray.com/2013/11/27/the-string-type-is-broken- 有时在处理字符串及其字节级组件时,您可能会无意中将字符成两半。
埃弗里特

Answers:


495

为了扩展其他人给出的答案:

我们有很多语言和许多字符,计算机应该理想地显示这些字符。Unicode为每个字符分配一个唯一的数字或代码点。

计算机处理字节等数字...在这里跳过一些历史,而忽略内存寻址问题,8位计算机会将8位字节视为硬件上容易表示的最大数字单位,而16位计算机将扩展到两个字节,依此类推。

诸如ASCII之类的旧字符编码来自(预)8位时代,并试图将当时计算中的主要语言(即英语)塞入0到127(7位)范围内的数字。字母表中有26个字母(大写和非大写形式),数字和标点符号,效果都很好。对于其他非英语语言,ASCII扩展了8位,但是此扩展提供的其他128个数字/代码点将根据所显示的语言映射到不同的字符。ISO-8859标准是此映射的最常见形式。ISO-8859-1和ISO-8859-15(也称为ISO-Latin-1,latin1,是的,还有8859 ISO标准的两个不同版本)。

但这还不足以表示一种以上语言的字符,因此将所有可用字符都塞进一个字节中是行不通的。

本质上有两种不同类型的编码:一种通过添加更多位来扩展值范围。这些编码的示例是UCS2(2字节= 16位)和UCS4(4字节= 32位)。它们的内在遭受与ASCII和ISO-8859标准相同的问题,因为它们的值范围仍然受到限制,即使该限制要高得多。

另一种编码类型是每个字符使用可变数量的字节,对此,最常见的编码是UTF编码。所有UTF编码的工作方式大致相同:您选择一个单位大小,对于UTF-8是8位,对于UTF-16是16位,对于UTF-32是32位。然后,标准将这些位中的一些位定义为标志:如果设置了这些位,则将一系列单元中的下一个单元视为同一字符的一部分。如果未设置,则本单位将完全代表一个字符。因此,最常见的(英文)字符在UTF-8中仅占用一个字节(在UTF-16中为两个字节,在UTF-32中为4个字节),而其他语言字符则可以占用六个字节或更多字节。

多字节编码(在上面的解释之后我应该说多单元编码)具有相对节省空间的优点,但是缺点是诸如查找子字符串,比较等操作都必须将字符解码为unicode代码在执行此类操作之前的要点(尽管有一些快捷方式)。

UCS标准和UTF标准都对Unicode中定义的代码点进行编码。从理论上讲,这些编码可用于编码任何数字(在编码支持的范围内)-但是,当然,这些编码是用来编码Unicode代码点的。那就是你之间的关系。

Windows将所谓的“ Unicode”字符串作为UTF-16字符串处理,而如今,大多数UNIX默认为UTF-8。由于UTF-8中的单位大小与ASCII中的单位大小相同,因此HTTP之类的通信协议往往最适合UTF-8,并且大多数此类协议都是在ASCII时代设计的。另一方面,UTF-16 在代表所有活动语言时可提供最佳的平均空间/处理性能。

Unicode标准定义的代码点少于32位表示的代码点。因此,出于所有实际目的,UTF-32和UCS4成为相同的编码,因为您不必在UTF-32中处理多单位字符。

希望填写一些细节。


9
从概念上讲,UCS-2和UCS-4是字符集,而不是字符编码(因此称为名称)。
机械蜗牛

74
@Tuukka这个帖子中的错误很多。有超过仅有2版本的ISO 8859. ASCII的没有为英文,却缺少一样东西弯引号,分迹象,口音,和一大堆更多-Unicode是不是只是非英语; 英语也需要!在ANY编码中,任何代码点都不超过4个字节;这个6字节的业务完全是错误的。您不能对任何Unicode标量值进行UTF编码,因为这表示:替代和其他66个非字符都被禁止。UCS-4和UTF-32不同。没有多单元UTF-32。UTF-16的效率不如他们假装的-&c&c&c!
tchrist 2011年

1
ASCII也不包含英镑符号£,当然也不包含欧元符号€(比ASCII还要年轻得多)。
TRiG 2012年

1
@tchrist看起来6个字节毕竟不可能的。参见:joelonsoftware.com/articles/Unicode.html,它表示从0x04000000to到字符空间0x7FFFFFFF,或者以二进制形式存在1111110v 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv -,实际上是6个字节。但是,最大为6个字节,而不是因为该文章令人困惑地声称“六个字节或更多 ”。
语法错误

12
@syntaxerror:“只有128和更高的代码点使用2、3存储,实际上最多6个字节。” 在编写时是准确的,但在同年晚些时候(十二年前)已失效。 en.wikipedia.org/wiki/UTF-8说:“原始规范涵盖了最多31位的数字(通用字符集的原始限制)。2003年11月,RFC 3629将UTF-8限制为以U +结尾10FFFF,以匹配UTF-16字符编码的限制。这删除了所有5字节和6字节序列,以及大约4字节序列的一半。”
Mooing Duck

237

让我用一个例子来说明这个话题:

A chinese character:      汉
it's unicode value:       U+6C49
convert 6C49 to binary:   01101100 01001001

到目前为止,还没有什么神奇的,这很简单。现在,假设我们决定将此字符存储在硬盘上。为此,我们需要以二进制格式存储字符。我们可以直接将其存储为“ 01101100 01001001”。做完了!

但是请稍等,“ 01101100 01001001”是一个字符还是两个字符?您知道这是一个字符,因为我告诉过您,但是当计算机读取它时,它就不知道了。因此,我们需要某种“编码”来告诉计算机将其视为一体。

这是“ UTF-8”规则出现的位置:http : //www.fileformat.info/info/unicode/utf8.htm

Binary format of bytes in sequence

1st Byte    2nd Byte    3rd Byte    4th Byte    Number of Free Bits   Maximum Expressible Unicode Value
0xxxxxxx                                                7             007F hex (127)
110xxxxx    10xxxxxx                                (5+6)=11          07FF hex (2047)
1110xxxx    10xxxxxx    10xxxxxx                  (4+6+6)=16          FFFF hex (65535)
11110xxx    10xxxxxx    10xxxxxx    10xxxxxx    (3+6+6+6)=21          10FFFF hex (1,114,111)

根据上表,如果我们要使用'UTF-8'格式存储此字符,则需要在我们的字符前加上一些'标题'。我们的汉字长16位(您自己计算二进制值),因此我们将在第3行使用格式,因为它提供了足够的空间:

Header  Place holder    Fill in our Binary   Result         
1110    xxxx            0110                 11100110
10      xxxxxx          110001               10110001
10      xxxxxx          001001               10001001

将结果写成一行:

11100110 10110001 10001001

这是汉字的UTF-8(二进制)值!(请自己确认:http : //www.fileformat.info/info/unicode/char/6c49/index.htm

摘要

A chinese character:      汉
it's unicode value:       U+6C49
convert 6C49 to binary:   01101100 01001001
embed 6C49 as UTF-8:      11100110 10110001 10001001

PS:如果您想在python中学习此主题,请单击此处


6
“但是等一下,'01101100 01001001'是一个字符还是两个字符?您知道这是一个字符,因为我告诉过您,但是当计算机读取它时,它就不知道了。因此我们需要某种“编码”来告诉计算机将其视为一体。” 好的,但是计算机仍然不知道应该用utf-8编码吗?
Koray Tugay,2015年

15
@KorayTugay计算机不知道应使用哪种编码。将字符保存到文件时以及从文件中读取字符时,都必须告诉它。

3
@Connor计算机不知道使用哪种格式。保存文档时,文本编辑器必须将其编码显式设置为utf-8或用户想要使用的任何格式。同样,当文本编辑器程序读取文件时,它需要选择一种文本编码方案以对其进行正确解码。在键入和输入字母时也是如此,文本编辑器需要知道使用哪种方案,以便正确保存。

2
那么这些标头如何解释?如果我看第一个表,那么我想:如果字节以位开头,0则字符由1 位表示(当前位);如果字节以位开头,110则字符由2个字节表示(当前位和下一个(10))后面的剩余位,如果字节1110以3 开头,则该字符由3个字节表示,即当前字节和接下来的2个字节(后面的剩余位10)。
JBoy

2
阅读有关UTF-8的10篇文章;读完这篇文章后,我在10秒钟内就明白了:)
jrhee17 '18

201

不幸的是,根据上下文,“ Unicode”以各种不同的方式使用。它最正确的用法(IMO)是用作编码字符集 -即一组字符以及这些字符与代表它们的整数代码点之间的映射。

UTF-8是字符编码-一种从字节序列转换为字符序列,反之亦然的方法。它涵盖了整个Unicode字符集。ASCII编码为每个字符一个字节,而其他字符则根据它们的确切代码点占用更多的字节(所有当前定义的代码点最多4个字节,即最多U-0010FFFF,实际上4个字节最多可以应付U-001FFFFF)。

如果将“ Unicode”用作字符编码的名称(例如,.NET Encoding.Unicode属性),则通常表示UTF-16,它将大多数常用字符编码为两个字节。一些平台(尤其是.NET和Java)使用UTF-16作为其“本机”字符编码。如果您需要担心无法在单个UTF-16值中编码的字符(它们被编码为“代理对”),则会导致毛茸茸的问题-但是大多数开发人员都不必为此担心,IME。

关于Unicode的一些参考:


16
我认为UTF-16在Windows平台上仅等于“ Unicode”。人们默认在* nix上使用UTF-8。+1虽然,很好的答案
jalf

10
@克里斯:不,ISO-8859-1 不是 UTF-8。UTF-8将U + 0080到U + 00FF编码为两个字节,而不是一个字节。视窗1252和ISO-8859-1是大部分相同,但它们的值0x80的和0x99之间的差异,如果我没有记错,在ISO 8859-1有一个“洞”,但CP1252定义字符。
乔恩·斯基特

13
将UTF-16称为“ Unicode”的想法因其可能引起混淆而使我感到不安-尽管明确指出这仅是.NET约定。UTF-16是表示Unicode的一种方式,但不是“ Unicode编码”。
thomasrutter

6
@unwesen:UTF-8不需要代理对。它只是使用逐渐变长的字节序列表示非BMP字符。
乔恩·斯基特

5
@RoyiNamir:是的,不幸的是,“ Unicode”经常被用来表示“ UTF-16”,尤其是在Windows中。
乔恩·斯基特

108

它们不是一回事-UTF-8是编码Unicode的一种特殊方式。

您可以根据应用程序和打算使用的数据选择很多不同的编码。据我所知,最常见的是UTF-8,UTF-16和UTF-32。


10
但是,关键是有些编辑建议将文件另存为“ Unicode”或“ UTF-8”。因此,我认为在这种情况下提及“ Unicode”是UTF-16。
serhio 2010年

71

Unicode仅定义代码点,即代表字符的数字。将这些代码点存储在内存中的方式取决于所使用的编码。UTF-8是编码Unicode字符的一种方法。


2
但是,关键是有些编辑建议将文件另存为“ Unicode”或“ UTF-8”。因此,我认为在这种情况下提及“ Unicode”是UTF-16。
serhio 2010年

表示字符的数字也使用ASCII。
Brighty 2014年

6
在查看此页面上的其余答案之前和之后阅读本
文档

33

Unicode是与ISO / IEC 10646一起定义通用字符集(UCS)的标准,它是表示几乎所有已知语言所需的所有现有字符的超集。

Unicode分配一个名称和一个数字(字符代码代码点)。

UTF-8编码是一种在计算机内存中数字表示这些字符的方法。UTF-8将每个代码点映射为一系列八位字节(8位字节)

例如

UCS字符= Unicode汉字字符

UCS代码点= U + 24B62

UTF-8编码= F0 A4 AD A2(十六进制)= 11110000 10100100 10101101 10100010(bin)


不,UTF-8仅将代码点映射到大于127的序列中。从0到127的所有内容都不是序列,而是单个字节。顺便说一句,ASCII还将字符的名称分配给数字,因此与Unicode相同。但是Unicode不会在代码点127处停止,而是上升到0x10ffff。
Brighty 2014年

2
@好吧,我不同。实际上,ASCII字符已映射到单个字节序列。第一位,对于ASCII字符代码为0,表示跟随的字节数为零。http://www.wikiwand.com/en/UTF-8#/Description看看第一行。
nightlytrails 2014年

对我来说,一个序列包含一个以上的字节。UTF-8中的ASCII字符按原样是一个字节,最高有效位设置为0。高于127的代码点需要序列,该序列始终具有一个起始字节和一个,两个或三个后续字节。那么,为什么将单个字节称为“序列”呢?
Brighty

好吧……很多时候,英语律师可能会因为故意滥用软件而感到困惑。这里也是一样。您可以对此进行争论。但这不会使它更加清晰。
nightlytrails 2014年

1
@brighty Hmmm,在数学中,由0个元素组成的序列可以。1个元素的序列在这里也可以。
chux-恢复莫妮卡

24

Unicode只是定义字符集(UCS)和编码(UTF)来对该字符集进行编码的标准。但通常,Unicode是指字符集而不是标准。

阅读所有软件开发人员的绝对最低限度,肯定要在5分钟内完全了解Unicode和字符集(无借口!)Unicode


1
@serhio:我知道。尽管存在三种不同的UTF-16编码:两个显式的UTF-16LEUTF-16BE以及隐式的UTF-16,其中的字节序由BOM来指定。
浓汤

@Gumbo:缺少BOM并不意味着它是不同的编码。只有两种编码。
Mooing Duck 2015年

上面的博客由Stakcoverflow的首席执行官撰写。
Shailesh Pratapwar

23

现有的答案已经解释了很多细节,但这是一个简短的答案,其中包含最直接的说明和示例。

Unicode是将字符映射到代码点的标准
每个字符都有一个唯一的代码点(标识号),这是一个数字,例如9731。

UTF-8是一种 编码的码点。
为了将所有字符存储在磁盘上(在文件中),UTF-8将字符分成最多4个八位字节(8位序列)-字节。UTF-8是几种编码(表示数据的方法)之一。例如,在Unicode中,(十进制)代码点9731表示一个雪人(),它由UTF-8中的3个字节组成:E2 98 83

这是带有一些随机示例排序列表


1
没有!UTF-8是编码Unicode字符的一种好方法,但是我们也可以使用UTF-16或UTF-32进行编码。使用UTF-32,DWORD与代码点之间具有1:1的关系,而使用UTF-16,仅BMP的代码点具有WORD与代码点之间的1:1关系,不包括替代和BOM。在UTF-8中,仅对于<127的代码点,字节和代码点之间就具有1:1的关系
。– Brighty

5
@brighty:是的,但是为什么要“不!”?我写了“ UTF-8是几种编码之一”,因为还有UTF-16和UTF-32。
basic6 2014年

16

1. Unicode

世界各地有很多字符,例如“ $,&,h,a,t,?,张,1,=,+ ...”。

然后是一个致力于这些角色的组织,

他们制定了一个称为“ Unicode”的标准。

标准如下:

  • 创建一个窗体,其中每个位置称为“代码点”或“代码位置”。
  • 整个位置从U + 0000到U + 10FFFF;
  • 到目前为止,某些职位已填充字符,其他职位已保存或为空。
  • 例如,位置“ U + 0024”用字符“ $”填充。

PS:当然,还有另一个名为ISO的组织正在维护另一个标准-“ ISO 10646”,几乎相同。

2. UTF-8

如上所述,U + 0024只是一个位置,因此我们无法在计算机中为字符“ $”保存“ U + 0024”。

必须有一种编码方法。

然后是编码方法,例如UTF-8,UTF-16,UTF-32,UCS-2...。

在UTF-8下,代码点“ U + 0024”被编码为00100100。

00100100是我们在计算机中为“ $”保存的值。


1
通常,UTF-8是当今任何人使用的唯一变体。
瑞克·詹姆斯

2
ISO 10646是与Unicode字符集相同的标准。Unicode定义了除字符集以外的许多其他内容,例如排序规则,大小写等。ISO10646只是字符集(目前有超过13万个字符集)。Unicode协会和ISO联合开发Unicode,而ISO仅关注字符集及其编码,而Unicode还定义了字符属性和处理文本的规则。
thomasrutter

12

我已经检查了Gumbo答案中的链接,并且想将其中的某些内容粘贴到Stack Overflow上。

“ ...有些人误以为Unicode只是一个16位代码,其中每个字符占用16位,因此有65,536个可能的字符。实际上,这是不正确的。这是关于Unicode的最常见的神话,因此,如果您认为那样,就不会感到难过。

实际上,Unicode对字符的思考方式有所不同,您必须了解Unicode对事物的思考方式,否则没有任何意义。

到目前为止,我们假设一个字母映射到一些可以存储在磁盘或内存中的位:

A-> 0100 0001

在Unicode中,字母映射到称为代码点的位置,这仍然只是一个理论上的概念。该代码点在内存或磁盘中的表示方式完全是另外一回事……”

“ ...... Unicode联盟为每个字母中的每个柏拉图字母分配了一个魔幻数字,其写法如下:U + 0639。该魔幻数字称为代码点。U+表示“ Unicode”,数字为十六进制。 U + 0639是阿拉伯字母Ain。英语字母A是U + 0041...。”

“ ...好,所以说我们有一个字符串:

你好

在Unicode中,它对应于以下五个代码点:

U + 0048 U + 0065 U + 006C U + 006C U + 006F。

只是一堆代码点。数字,真的。关于如何将其存储在内存中或以电子邮件形式表示,我们还没有说什么。”

“ ...就是编码的来源。

Unicode编码的最早想法导致了两个字节的神话,嘿,让我们将这些数字分别存储在两个字节中。所以你好成为

00 48 00 65 00 6C 00 6C 00 6F

对?没那么快!也可能不是:

48 00 65 00 6C 00 6C 00 6F 00?...”


在ASCII中,字母也映射到代码点,而不仅仅是unicode。
Brighty 2014年

8

UTF-8是一种可能的Unicode编码方案文本。

统一码是一种广泛的标准,它定义了130,000个字符并为每个字符分配一个数字代码(一个代码点)。它还定义了有关如何对该文本进行排序,对其进行规范化,更改其大小写等的规则。Unicode中的字符由从零到0x10FFFF(含零)的代码点表示,尽管有些代码点是保留的,不能用于字符。

字符串Unicode代码点可以被编码成二进制流的方式不止一种。这些称为“编码”。最简单的编码是UTF-32,它简单地将每个代码点存储为32位整数,每个宽度为4个字节。

UTF-8是另一种编码,由于与UTF-32等相比具有许多优点,因此已成为事实上的标准。UTF-8编码为单字节值序列。每个代码点可以使用可变数量的这些字节值。ASCII范围内的代码点被裸编码,以与ASCII兼容。超出此范围的代码点使用可变数量的字节(2、3或4),具体取决于它们位于的范围。

UTF-8在设计时考虑了以下属性:

  • ASCII字符的编码方式与ASCII完全相同,因此ASCII字符串也是有效的UTF-8字符串。

  • 二进制排序:使用朴素的二进制排序对UTF-8字符串进行排序仍将导致所有代码点均按数字顺序排序。

  • 需要多个字节的字符不包含ASCII范围内的任何字节值,以确保它们的一部分不会被误认为ASCII字符。这也是一项安全功能。

  • UTF-8易于验证,并且可以通过验证器与其他字符编码区分开。其他8位或多字节编码的文本也很少会验证为UTF-8。

  • 随机访问:在UTF-8字符串中的任何一点上,都可以判断该位置的字节是否是一个字符的第一个字节,并可以找到下一个或当前字符的开始,而无需向前扫描或向后超过几个字节,或者在流的开头读取任何内容。


有几点要点:[1]不应将“ ASCII字符完全按照ASCII编码改为 “ ASCII字符完全按照UTF-8编码”吗?[2] 对我来说,短语“ Unicode中的代码...”尚不清楚。您是说“ Unicode代码点...”吗?
skomisa

@skomisa对于第1点,我的意思是ASCII范围内的字符编码对于ASCII和UTF-8是相同的。
thomasrutter

对于第2点,这是一个公平的点,我将对其进行编辑以使其更清楚
thomasrutter

2

他们是同一回事,不是吗?

不,不是。


我认为您所引用的Wikipedia页面的第一句话给出了一个不错的简短摘要:

UTF-8是一种可变宽度的字符编码,能够使用1到4个8位字节来编码Unicode中的所有1,112,064个有效代码点。

详细说明:

  • Unicode是一种标准,它定义了从字符到数字的映射,即所谓的代码点,(如下面的示例所示)。有关完整的映射,您可以在这里查看

    ! -> U+0021 (21),  
    " -> U+0022 (22),  
    \# -> U+0023 (23)
    
  • UTF-8是在编码这些代码点的方式之一的形式的计算机可以理解的,又名比特。换句话说,这是一种将这些代码点中的每一个转换为比特序列或将一系列比特转换为等效代码点的方法/算法。请注意,有许多其他的Unicode编码。


乔尔给出了一个非常好的解释和历史的概况这里


2

如果我可以总结一下我从该线程收集的信息:

Unicode将字符“转换” 为序数(十进制形式)

à = 224

UTF-8是一种将这些数字 “转换” 为二进制表示形式的编码。

224 = 11000011 10100000

请注意,我们正在谈论的是224 的二进制表示形式,而不是其二进制形式,即0b11100000。


2

本文介绍了所有详细信息 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.

在此处输入图片说明


非常好的链接文章,希望它继续保持活跃
yolob 25

0

UTF-8是一种使用8位序列编码Unicode字符的方法。

Unicode是一种代表来自多种语言的多种字符的标准。


4
“ 8位序列”…?可能想更精确地指定…
deceze
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.