ChatGPT解决这个技术问题 Extra ChatGPT

UTF-8 和 Unicode 有什么区别?

根据 Wikipedia UTF-8 页面,我从人们那里听到了相互矛盾的意见。

它们是一样的,不是吗?有人可以澄清吗?

在我看来,这个 WIKI 关于 unicode 和 UTF 的内容是可以的。对此的一些评论很奇怪:“在 UTF-8(或任何其他多字节编码)中,有可能在字符中间拆分或截断字符串,这可能会导致字符串无效。”因此,经过 UTF-8 编码的字符串不再是字符串,而是字节数组或字节流。组成字符串的字符被编码。当然也可以解码。现在你当然可以在起始字节或下一个字节之后剪切一个 utf-8 序列,但为什么有人要这样做呢?
这篇关于字符串数据类型的文章具有教育意义:mortoray.com/2013/11/27/the-string-type-is-broken -- 有时在处理字符串及其字节级组件时,您可能会无意中将一个字符切成两半。
@brighty 如果该字节流通过 packetises 的网络传输,那么字符串可能会被拆分为两个数据包 - 即在 UTF-8 边界以外的位置(即 下一个 字节不是 MSBits 为 011011101111010)...
@SlySven您谈论字节流还是字符串?当然,一个字节流可以分成两个数据包,但 TCP 的工作是在目的地重新创建难题,例如每个数据包都有其序列号,并且接收器确实确认收到的每个数据包。当然,如果 TCP/IP 会话不正常地断开连接,则只有部分 - 比如说 utf-8 编码的字节流 - 到达目的地。
两个都!我主要为 MUD 客户端应用程序编写代码,并且在没有额外(所谓的“Go-Ahead”或“End-of-record”)信号的情况下,数据包可以并且确实在它们穿过 Internet 时被拆分 - 如果客户端等待任何进一步的数据包的时间不够长......

S
Scott Tesler

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

我们有很多语言,有很多字符,计算机应该理想地显示。 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。 HTTP 等通信协议往往最适合 UTF-8,因为 UTF-8 中的单位大小与 ASCII 中的相同,并且大多数此类协议都是在 ASCII 时代设计的。另一方面,UTF-16 在表示所有现存语言时提供了最佳的平均空间/处理性能。

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

希望补充一些细节。


从概念上讲,UCS-2 和 UCS-4 是字符集,而不是字符编码(因此得名)。
@Tuukka 此帖子中的错误很多。 ISO 8859 不仅有 2 个版本。ASCII 不适用于英语,缺少像大引号、分号、重音符号等的东西——Unicode 不仅仅是非英语;英语也需要!!在任何编码中,没有代码点占用超过 4 个字节;这个 6 字节的业务是完全错误的。您不能对任何 Unicode 标量值进行 UTF 编码,因为这说明:代理和其他 66 个非字符都被禁止。 UCS-4 和 UTF-32 不一样。没有多单元 UTF-32。 UTF-16 并不像他们想象的那么高效——&c&c&c!
ASCII 也不包含英镑符号 £,当然也不包含欧元符号 €(它比 ASCII 小得多)。
@tchrist 看起来 6 个字节 毕竟不是不可能的。请参阅:joelonsoftware.com/articles/Unicode.html 表示从 0x040000000x7FFFFFFF 存在一个字符空间,或者在二进制中它是 1111110v 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv - 这确实是 6 个字节。但是,最大是 6 个字节,而不是文章混淆地声称“六个字节或更多”。
@syntaxerror:“只有 128 及以上的代码点使用 2、3 存储,实际上最多 6 个字节。”写的时候是准确的,但同年晚些时候(十二年前)它被无效了。 en.wikipedia.org/wiki/UTF-8 说“原始规范涵盖最多 31 位的数字(通用字符集的原始限制)。2003 年 11 月,UTF-8 受到 RFC 3629 的限制,以 U+10FFFF 结尾,以匹配约束UTF-16 字符编码。这删除了所有 5 和 6 字节序列,以及大约一半的 4 字节序列。
M
Matthias Braun

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

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 格式存储这个字符,我们需要在我们的字符前面加上一些“标题”。我们的汉字是 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二进制值!亲眼看看: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

PS 如果您想在 Python 中学习此主题,click here


“但是等一下,‘01101100 01001001’是一个字符还是两个字符?你知道这是一个字符,因为我告诉过你,但是当计算机读取它时,它不知道。所以我们需要某种“编码”来告诉计算机将其视为一体。”好吧,但是计算机仍然不知道它应该用 utf-8 编码吗?
@KorayTugay 计算机不知道它应该使用什么编码。当您将字符保存到文件以及从文件中读取字符时,您必须告诉它。
@Connor 计算机不知道使用什么格式。保存文档时,文本编辑器必须明确将其编码设置为 utf-8 或用户想要使用的任何格式。此外,当文本编辑器程序读取文件时,它需要选择文本编码方案才能正确解码。当您键入和输入字母时也是如此,文本编辑器需要知道您使用什么方案才能正确保存它。
那么这些标题是如何解释的呢?如果我查看第一个表,然后我想:如果字节以位 0 开头,则字符由 1 位(当前位)表示,如果字节以 110 开头,则字符由 2 个字节表示(当前和下一个(10 之后的剩余位)),如果字节以 1110 开头,则字符由 3 个字节表示,当前和接下来的 2 个字节(10 之后的剩余位)。
阅读 10 篇关于 UTF-8 的文章;读完这篇我在 10 秒内就明白了:)
A
Andrew Tobilko

不幸的是,根据上下文,“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 的一些参考资料:

Unicode 联盟网站,尤其是教程部分

乔尔的文章

我自己的文章(面向.NET)


我认为 UTF-16 在 Windows 平台上只等于“Unicode”。人们倾向于在 *nix 上默认使用 UTF-8。 +1 不过,很好的答案
@Chris:不,ISO-8859-1 不是 UTF-8。 UTF-8 将 U+0080 编码为 U+00FF 为两个字节,而不是一个。 Windows 1252 和 ISO-8859-1 基本相同,但如果我没记错的话,它们在值 0x80 和 0x99 之间有所不同,其中 ISO 8859-1 有一个“洞”,但 CP1252 定义了字符。
将 UTF-16 称为“Unicode”的想法让我感到不安,因为它可能会造成混淆——尽管这被明确指出只是一个 .NET 约定。 UTF-16 是一种表示 Unicode 的方式,但它不是“Unicode 编码”。
@unwesen:UTF-8 不需要代理对。它只是使用逐渐变长的字节序列来表示非 BMP 字符。
@RoyiNamir:是的,不幸的是,“Unicode”经常被用来表示“UTF-16”,尤其是在 Windows 中。
G
Greg

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

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


然而,关键是一些编辑建议将文件保存为“Unicode”或“UTF-8”。因此,我认为在这种情况下提到“Unicode”是 UTF-16 是必要的。
M
Martin Cote

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


然而,关键是一些编辑建议将文件保存为“Unicode”或“UTF-8”。因此,我认为在这种情况下提到“Unicode”是 UTF-16 是必要的。
表示字符的数字也执行 ASCII。
s
syntaxerror

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

Unicode 为其曲目中的每个字符分配一个名称和一个数字(字符代码或代码点)。

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

例如,

UCS 字符 = Unicode Han Character

UCS 代码点 = U+24B62

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


不,UTF-8 仅将代码点映射到大于 127 的序列中。从 0 到 127 的所有内容都不是序列,而是单个字节。顺便说一句,ASCII 还将字符的名称分配给数字,所以这与 Unicode 的作用相同。但是 Unicode 并没有在代码点 127 处停止,而是上升到 0x10ffff。
@brightly 我不一样。 Ascii 字符确实映射到单个字节序列。第一位,在 ascii 字符代码的情况下为 0,表示后面有多少字节 - 零。 http://www.wikiwand.com/en/UTF-8#/Description 看看第一行。
对我来说,一个序列包含多个字节。 UTF-8 中的 ASCII 字符是单个字节,最高有效位设置为 0。高于 127 的代码点需要序列,该序列始终具有一个起始字节和一个、两个或三个后续字节。那么为什么将单个字节称为“序列”?
嗯... 很多时候,英语语言的律师可能会对它在软件中的故意滥用感到困惑。这里也是同样的情况。你可以为此争论。但这不会使它更清楚。
@brighty 嗯,在数学中,sequence of 0 元素可以。 1个元素的序列在这里也很好。
G
Gumbo

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

阅读 The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)Unicode In 5 Minutes


@serhio:我知道。尽管存在三种不同的 UTF-16 编码:两种显式 UTF-16LE 和 UTF-16BE 以及隐式 UTF-16,其中字节顺序由 BOM 指定。
@Gumbo:缺少 BOM 并不意味着它是不同的编码。只有两种编码。
t
thomasrutter

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

Unicode 是一个范围广泛的标准,它定义了超过 140,000 个字符并为每个字符分配一个数字代码(一个代码点)。它还定义了如何对文本进行排序、规范化、更改大小写等规则。 Unicode 中的字符由从零到 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 可以很容易地验证,并通过验证器与其他字符编码区分开来。由于 UTF-8 的特殊结构,其他 8 位或多字节编码的文本也很少会验证为 UTF-8。

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


几个小问题: [1] 不应该将“ASCII 字符完全按照 ASCII 编码”改为“ASCII 字符完全按照 UTF-8 编码”吗? [2] “Unicode 中的代码......”这句话不清楚(对我来说)。你的意思是“Unicode代码点......”?
@skomisa 对于第 1 点,我的意思是 ASCII 范围内的字符编码对于 ASCII 和 UTF-8 是相同的。
对于第 2 点,这是一个公平的观点,我将对其进行编辑以使其更清晰
C
Community

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

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

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

这是一个sorted list with some random examples


不! UTF-8 是编码 unicode 字符的好方法,但我们也可以使用 UTF-16 或 UTF-32 进行编码。对于 UTF-32,我们在 DWORD 和代码点之间有 1:1 的关系,对于 UTF-16,我们在 WORD 和代码点之间有 1:1 的关系,仅适用于 BMP 的代码点,不包括代理项和 BOM。在 UTF-8 中,仅当代码点 < 127 时,字节和代码点之间存在 1:1 的关系。
@brighty:是的,但为什么“不!”?我写了“UTF-8 是几种编码之一”,因为还有 UTF-16 和 UTF-32。
t
thomasrutter

1.统一码

世界上有很多字符,例如“$,&,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 是我们在计算机中为“$”保存的值。


一般来说,UTF-8 是当今人们使用的唯一变体。
ISO 10646 是与 Unicode 字符集相同的标准。 Unicode 定义了字符集以外的很多东西,例如排序规则、大小写等。ISO 10646 只是字符集(目前有超过 130,000 个)。 Unicode 联盟和 ISO 共同开发 Unicode,其中 ISO 只关注字符集及其编码,而 Unicode 还定义了处理文本的字符属性和规则。
r
remykarem

如果我可以总结一下我从这个线程中收集到的内容:

Unicode 将字符分配给序数(以十进制形式)。 (这些数字称为代码点。)

à -> 224

UTF-8 是一种将这些序数(十进制形式)“翻译”为二进制表示的编码。

224 -> 11000011 10100000

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


P
Peter Mortensen

我已经检查了 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 中。
I
InGeek

这篇文章解释了所有细节http://kunststube.net/encoding/

写入缓冲区

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

00000000 11100011 10000001 10000010

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

00000000 00000000 00110000 01000010

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

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

从缓冲区读取

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

例如,如果您将以下内容解码:00000000 11100011 10000001 10000010 为 UTF16 编码,您将得到 而不是

注意: 编码和 Unicode 是两个不同的东西。 Unicode 是大的 (table),每个符号都映射到一个唯一的代码点。例如 符号(字母)有一个 (code point)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.

https://i.stack.imgur.com/6C0C6.png


非常好的链接文章,希望它继续保持活跃
对于UTF-8中的那个汉字,为什么保存为3个字节而不是2个,与UTF-16格式相同?
n
nightboy

在阅读了有关此主题的大量帖子和文章后,我的解释是:

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 编号。例如,字符“❤”具有以下值,即 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 个字节是单独阅读或一起阅读。

将这个“汉”字符的 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)

上表中的“x”字符代表“Free Bits”的数量,这些位是空的,我们可以写入它们。其他位为 UTF-8 格式保留,它们用作标题/标记。多亏了这些标头,当使用 UTF-8 编码读取字节时,计算机知道要一起读取哪些字节以及分别读取哪些字节。在使用 UTF-8 格式编码后,字符的字节大小取决于您需要写入多少位。在我们的例子中,“汉”字符正好是 2 个字节或 16 位:“01101100 01001001”因此我们的字符在编码为 UTF-8 后的大小将是 3 个字节或 24 位“11100110 10110001 10001001”,因为“3 UTF-8 bytes”有 16 个空闲位,我们可以将其写入解决方案,步骤如下:

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 编码区别的原始解释:https://javarevisited.blogspot.com/2015/02/difference-between-utf-8-utf-16-and-utf.html

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 位。


你怎么能发现 正好是 2 个字节或 16 位: 01101100 01001001 。在 Unicode 中。我可以看看桌子吗?
D
Dimos

它们是一样的,不是吗?

不,他们不是。

我认为您引用的 Wikipedia page 的第一句话给出了一个很好的简短摘要:

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

详细说明:

Unicode 是一种标准,它定义了从字符到数字的映射,即所谓的代码点(如下例所示)。对于完整的映射,您可以在这里查看。 ! -> U+0021 (21), " -> U+0022 (22), \# -> U+0023 (23)

UTF-8 是以计算机可以理解的形式(即位)对这些代码点进行编码的方法之一。换句话说,这是一种将这些代码点中的每一个转换为位序列或将位序列转换为等效代码点的方法/算法。请注意,Unicode 有很多替代编码。

Joel 给出了一个非常好的解释和对历史 here 的概述。


a
akaMahesh

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

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


“8 位序列”……?可能想更准确地指定...
“8 位序列”的意思是,它可以呈现 8 位格式。像这些 0100000111010011 1000010111100101 10100011 1000011011110001 10110001 10000010 10110001 。如您所见,对于 UTF-8,它可以是最小 1 个字节,最大可以是 4 个字节。
请注意,当您想使用 1 个字节时,第一个数字是 0 。当您想使用 2byte 时,前 3 位是 110。当您想使用 3 字节时,前 4 位是 1110。当您想使用 4 字节时,前 5 位是 11110。嗯。你明白了吗? :)
L
Luis Lavaire.

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

Unicode 是用于表示来自许多人类语言的字符的标准。

UTF-8 是一种编码 Unicode 字符的方法。

* 是的:我故意忽略了 UTF-8 的内部工作原理。


n
not2qubit

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

在这里,我列出了一些有用的单行代码。

在 Powershell 中:

# 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