UTF-8和Unicode有什么区别?

我从人们那里听到了相互矛盾的意见——根据维基百科utf - 8页面。

它们是一样的,不是吗?有人能澄清一下吗?

355563 次浏览

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

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

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

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

当“Unicode”被用作字符编码的名称(例如,作为。net 编码。Unicode属性)时,它通常表示utf - 16,它将大多数常见字符编码为两个字节。一些平台(特别是。net和Java)使用UTF-16作为它们的“原生”字符编码。如果您需要担心不能在单个UTF-16值中编码的字符(它们被编码为“代理对”),这将导致一些棘手的问题——但大多数开发人员从不担心这一点,IME。

关于Unicode的一些参考:

Unicode只定义了代码点,即一个代表字符的数字。如何在内存中存储这些代码点取决于您正在使用的编码。UTF-8是编码Unicode字符的一种方式。

Unicode只是一个标准,它定义了一个字符集(UCS)和编码(UTF)来编码这个字符集。但一般来说,Unicode指的是字符集,而不是标准。

阅读每个软件开发人员必须绝对、肯定地了解Unicode和字符集(没有借口!)五分钟内统一码

扩展一下其他人给出的答案:

我们有许多语言和许多字符,计算机应该理想地显示这些字符。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中占用四个字节),但其他语言字符可以占用六个字节或更多。

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

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

Windows将所谓的“Unicode”字符串处理为UTF-16字符串,而现在大多数unix默认为UTF-8。像HTTP这样的通信协议最适合使用UTF-8,因为UTF-8中的单位大小与ASCII中的单位大小相同,而且大多数此类协议都是在ASCII时代设计的。另一方面,UTF-16在表示所有现存语言时提供了最好的平均空间/处理性能。

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

希望这能补充一些细节。

我已经检查了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 ?……”

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

Unicode为其保留库中的每个字符分配一个名称和一个数字(字符代码码点)。

utf - 8编码是在计算机内存中以数字形式表示这些字符的一种方法。UTF-8将每个码位映射到一个八字节序列(8位字节)

,例如,

UCS字符= Unicode汉字

UCS代码点= U+24B62

UTF-8 encoding = F0 A4 AD A2 (hex) = 11110000 10100100 10101101 10100010 (bin)

现有的答案已经解释了很多细节,但这里有一个非常简短的答案,有最直接的解释和例子。

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

这是用一些随机的例子进行排序

1. Unicode

有很多世界各地的字符,如“$,,h,, t, ?,张,1 = +……”。

然后出现了一个致力于这些角色的组织,

他们制定了统一码标准。

标准如下:

  • 创建一个表单,其中每个位置都称为“代码点”或“代码位置”。
  • 整个位置从U+0000到U+10FFFF;
  • 到目前为止,有些位置被字符填充,有些位置被保存或为空。
  • 例如,位置“U+0024”被字符“$”填充。

PS:当然,还有另一个叫做ISO的组织维护着另一个标准——“iso10646”,几乎是一样的。

2. utf - 8

如上所述,U+0024只是一个位置,所以我们不能将“U+0024”在电脑中保存为字符“$”。

必须有一种编码方法。

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

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

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

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

A Chinese character:      汉
its Unicode value:        U+6C49
convert 6C49 to binary:   01101100 01001001

目前还没有什么神奇的,很简单。现在,假设我们决定将这个字符存储在硬盘驱动器上。为此,我们需要以二进制格式存储字符。我们可以简单地将其存储为'01101100 01001001'。完成了!

但是等一下,'01101100 01001001'是一个字符还是两个字符?你知道这是一个字符,因为我告诉过你,但当计算机读取它时,它不知道。所以我们需要某种编码来告诉计算机把它当做一个。

这就是UTF-8规则的用武之地:https://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格式存储这个字符,我们需要给我们的字符加上一些'headers'前缀。我们的中文字符有16位长(你自己计算二进制值),所以我们将在第三行使用该格式,因为它提供了足够的空间:

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二进制值的汉字!你自己看看:https://www.fileformat.info/info/unicode/char/6c49/index.htm

总结

A Chinese character:      汉
its Unicode value:        U+6C49
convert 6C49 to binary:   01101100 01001001
encode 6C49 as UTF-8:     11100110 10110001 10001001

附注:如果你想用Python学习这个主题,点击这里

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

Unicode是一个范围广泛的标准,它定义了超过140,000个字符,并为每个字符分配一个数字代码(一个码位)。它还定义了如何对文本进行排序、规范化、更改大小写等规则。Unicode中的字符由一个从0到0x10FFFF(包括0x10FFFF)的码位表示,但有些码位是保留的,不能用于字符。

将一串Unicode码位编码成二进制流的方法不止一种。这些被称为“编码”。最直接的编码是utf - 32,它将每个代码点存储为32位整数,每个整数宽为4字节。因为代码点最多只能到0x10FFFF(需要21位),所以这种编码有点浪费。

utf - 8是另一种编码,由于与UTF-32和其他编码相比有许多优点,它正在成为事实上的标准。UTF-8将每个码位编码为1、2、3或4个字节值的序列。ASCII范围内的码位被编码为一个单字节值,以便与ASCII兼容。超出这个范围的代码点分别使用2、3或4个字节,这取决于它们所在的范围。

UTF-8在设计时考虑了这些属性:

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

  • 更高效:UTF-8中的文本字符串几乎总是比UTF-32或UTF-16中的相同字符串占用更少的空间,只有少数例外。

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

  • 当一个码位使用多个字节时,这些字节都不包含ASCII范围内的值,以确保它们的任何部分都不会被误认为是ASCII字符。这也是一个安全特性。

  • UTF-8可以很容易地验证,并通过验证器与其他字符编码区分开来。其他8位或多字节编码中的文本也很少被验证为UTF-8,因为UTF-8的结构非常特殊。

  • 随机访问:在UTF-8字符串中的任何一点,都有可能判断该位置的字节是否是字符的第一个字节,并找到下一个或当前字符的开始,而不需要向前或向后扫描超过3个字节,也不需要知道我们开始读取的字符串有多远。

它们是一样的,不是吗?

不,他们不是。


我认为你引用的维基百科页面的第一句话给出了一个很好的,简短的总结:

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

阐述:

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

    ! -> U+0021 (21),
    " -> U+0022 (22),
    \# -> U+0023 (23)
    
  • UTF-8 is one of the ways to encode these code points in a form a computer can understand, aka bits. In other words, it's a way/algorithm to convert each of those code points to a sequence of bits or convert a sequence of bits to the equivalent code points. Note that there are a lot of alternative encodings for Unicode.


Joel gives a really nice explanation and an overview of the history here.

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

Unicode是表示来自多种语言的各种字符的标准。

如果我可以总结一下我从这篇文章中收集到的信息:

Unicode将字符赋值为序数(十进制形式)。(这些数字被称为码位。)

à -> 224

UTF-8是一种“转换”这些序数(十进制形式)二进制< em > < / em >表示的编码。

224 -> 11000011 10100000

注意,我们讨论的是224的二进制数表示,而不是它的二进制形式0b11100000。

这篇文章解释了所有的细节 # EYZ0 < / p >

写入缓冲区

如果你写入一个4字节的缓冲区,符号 UTF8编码,你的二进制将看起来像这样:

00000000 11100011 10000001 10000010

如果你写入一个4字节的缓冲区,符号与UTF16编码,你的二进制将看起来像这样:

00000000 00000000 00110000 01000010

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

例:对于这个特殊的符号: UTF16编码更有效,因为我们有2个空闲字节用于下一个符号。但这并不意味着你必须使用UTF16来表示日本字母。

从缓冲区读取

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

< p >。如果你解码这个: 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

作为一个直截了当的简单回答:

  • Unicode是一种表示多种人类语言字符的标准。
  • UTF-8是一种编码Unicode字符的方法。

* # EYZ0

所以你通常从谷歌到这里,想要尝试不同的东西。
但是如何打印和转换所有这些字符集呢?< / p >

这里我列出了一些有用的一行程序。

在# EYZ0:

# Print character with the Unicode point (U+<hexcode>) using this:
[char]0x2550


# With Python installed, you can print the unicode character from U+xxxx with:
python -c 'print(u"\u2585")'

如果你有更多的Powershell trix或快捷方式,请评论。

Bash中,你会欣赏libiconvutil-linux包中的iconvhexdumpxxd(可能在其他*nix发行版中命名不同)。

# To print the 3-byte hex code for a Unicode character:
printf "\\\x%s" $(printf '═'|xxd -p -c1 -u)
#\xE2\x95\x90


# To print the Unicode character represented by hex string:
printf '\xE2\x96\x85'
#▅


# To convert from UTF-16LE to Unicode
echo -en "════"| iconv -f UTF-16LE -t UNICODEFFFE


# To convert a string into hex:
echo -en '═�'| xxd -g 1
#00000000: e2 95 90 ef bf bd


# To convert a string into binary:
echo -en '═�\n'| xxd -b
#00000000: 11100010 10010101 10010000 11101111 10111111 10111101  ......
#00000006: 00001010


# To convert a binary string into hex:
printf  '%x\n' "$((2#111000111000000110000010))"
#e38182


在阅读了大量关于这个话题的帖子和文章后,我的解释是:

1 - Unicode字符表

“Unicode"是一个巨大的表,它有21位宽,这21位提供了1,114,112个码位/值/字段/位置来存储字符。

在这1,114,112个码点中,1,111,998个可以存储Unicode字符, 因为有2048个码点保留为代理,66个码点保留为非字符。 所以,有1,111,998个码位可以存储唯一的字符、符号、表情符号等 然而,到目前为止,在这1,114,112个码点中,只有144,697个被使用。 这144,697个代码点包含了涵盖所有语言的字符,以及符号、表情符号等

“统一码”中的每个字符;被分配给一个特定的码点,也就是有一个特定的值/ Unicode号。 例如字符“&;❤"”,有以下值即Unicode数字“&;U+2764" 值“;U+2764"从1,114,112个代码点中选取一个。 值“;U+2764"看起来像二进制:"11100010 10011101 10100100",正好是3个字节或24位(没有两个空格字符,每个空格都占用1位,但我只是为了视觉目的添加它们,为了使24位更可读,所以请忽略它们)

现在,我们的计算机应该如何知道这3个字节“11100010 10011101 10100100”;是分开读还是一起读?如果将这3个字节分别读取,然后转换为字符,结果将是"Ô, Ø, ñ",这与我们的心形表情符号"❤"有很大的不同。

2 -编码标准(UTF-8, ISO-8859, Windows-1251等)

为了解决这个问题,人们发明了编码标准。 自2008年以来,最流行的是UTF-8。UTF-8平均占所有网页的97.6%,这就是为什么我们将UTF-8,如下例所示

2.1 -什么是编码?

编码,简单地说就是将某物从一种东西转换为另一种东西。 在我们的例子中,我们正在将数据,更确切地说是字节转换为UTF-8格式, 我还想将这句话重新表述为:“将字节转换为UTF-8字节”,尽管它在技术上可能不正确

2.2一些关于UTF-8格式的信息,以及为什么它如此重要

UTF-8使用最小1字节存储字符,最大4字节存储字符。 多亏了UTF-8格式,我们可以拥有包含1字节以上信息的字符

这是非常重要的,因为如果不是UTF-8格式,我们就不可能有如此丰富的字母多样性,因为一些字母的字母不能装进1个字节,我们也不会有表情符号,因为每个表情符号至少需要3个字节。我很确定你现在已经明白了,让我们继续。

2.3汉字编码为UTF-8举例

现在,假设我们有一个汉字“汉”。

这个字符需要16个二进制位“01101100 01001001”,因此正如我们上面所讨论的,除非我们将它编码为UTF-8,否则我们无法读取这个字符,因为计算机将无法知道这两个字节是分开读取还是一起读取。

将此"汉"字符的2个字节,我喜欢称之为UTF-8字节,将导致以下结果:

(普通字节)“01101100 01001001"→(UTF-8编码字节)"11100110 10110001 10001001"

现在,我们是如何得到3个字节而不是2个字节的呢?这怎么可能是UTF-8编码,把2个字节变成3个字节?

为了解释UTF-8编码是如何工作的,我将逐字复制@MatthiasBraun的回复,非常感谢他的精彩解释。

2.4 UTF-8编码是如何工作的?

这里有一个将字节编码为UTF-8的模板。这就是编码是如何发生的,如果你问我的话,我觉得非常令人兴奋!

现在,仔细看看下面的表格,然后我们将一起浏览它。

        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)
    <李> < p >“x"上表中的字符代表“免费”的数量

    . Bits",这些位是空的,我们可以写入它们
  1. 其他位为UTF-8格式保留,它们被用作 头文件/标记。多亏了这些头文件,当字节被 如果使用UTF-8编码,计算机就知道该读取哪个字节 Together和which分开。

  2. 字符的字节大小,在使用UTF-8格式编码后, 取决于你需要写多少位。

  • 在我们的例子中,"汉"字符是2字节或16位:

  • < p >“01101100 01001001“

  • 因此我们的字符在编码为UTF-8后的大小将是3字节或24位

  • "11100110 10110001 10001001"

  • 因为“3 UTF-8字节”;有16个自由比特,我们可以写

  1. 解决方案,步骤如下:

2.5解决方案:

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

2.6简介:

        A Chinese character:      汉
its Unicode value:        U+6C49
convert 6C49 to binary:   01101100 01001001
encode 6C49 as UTF-8:     11100110 10110001 10001001

3 - UTF-8, UTF-16和UTF-32之间的区别

UTF-8、UTF-16和UTF-32编码之间差异的原始解释: # EYZ0 < / p >

UTF-8、UTF-16和UTF-32字符编码的主要区别在于它们在内存中表示一个字符所需的字节数:

UTF-8至少使用1个字节,但如果字符更大,则可以使用2、3或4个字节。 UTF-8也与ASCII表兼容

UTF-16至少使用2个字节。UTF-16不能占用3个字节,它可以占用2或4个字节。

. UTF-16不兼容ASCII表

UTF-32总是使用4个字节。

记住:UTF-8和UTF-16是变长编码, 其中UTF-8可以占用1到4个字节, 而UTF-16可以占用2或4个字节。 UTF-32是一个固定宽度的编码,它总是32位