UTF-8和Unicode有什么区别?

733

我听到了许多人的不同意见 - 根据维基百科 UTF-8页面。

它们不是一个东西吗?有人能澄清一下吗?


1
这个WIKI关于Unicode和UTF的写法我认为还不错。其中一些评论有点奇怪:“在UTF-8(或任何其他多字节编码)中,可以在字符中间拆分或截断字符串,这可能导致无效字符串。”因此,一个被UTF-8编码的字符串不再是一个字符串,而是一个字节数组或字节流。组成字符串的字符被编码了。当然,它也可以被解码。当然,你可以在起始字节或后续字节之后剪切utf-8序列,但是为什么有人要这样做呢? - brighty
1
这篇关于字符串数据类型的文章非常有教育意义:https://mortoray.com/2013/11/27/the-string-type-is-broken/ -- 有时候在处理字符串及其字节级组件时,您可能会无意中将一个字符切成两半。 - Everett
如果该字节流正在通过网络进行分组传输,则字符串可能会被拆分成两个数据包 - 即在不是UTF-8边界(即下一个字节不是具有MSBits为011011101111010的字节)的地方。@brighty - SlySven
@SlySven 你是在讨论字节流还是字符串?当然,一个字节流可以被分成两个数据包,但这是TCP的工作,在目标端重新组合这个谜题,例如每个数据包都有其序列号,接收方会确认每个已接收的数据包。当然,如果TCP/IP会话不正常断开连接,则只有部分 - 比如UTF-8编码的字节流 - 到达目的地。 - brighty
我主要为MUD客户端应用编写代码,在缺少额外的(所谓的“Go-Ahead”或“End-of-record”)信号的情况下,数据包在穿越互联网时可能会被分割 - 如果客户端没有等待足够长的时间以获取任何进一步的数据包... - SlySven
18个回答

19

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 就是我们在计算机中保存为字符“$”所使用的值。


2
一般来说,UTF-8 是今天任何人都使用的唯一变体。 - Rick James
3
ISO 10646是与Unicode字符集完全相同的标准。Unicode定义了许多除字符集之外的内容,例如排序规则、大小写等。ISO 10646仅仅是字符集(目前超过130,000个字符)。Unicode联盟和ISO共同开发Unicode,其中ISO只关注字符集及其编码,而Unicode还定义了字符属性和文本处理规则。 - thomasrutter

16

本文解释了所有细节: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.

这里输入图片描述


1
非常好的链接文章,希望它能继续保持活跃。 - yolob 21
为什么UTF-8编码中的中文字符要保存为3个字节而不是和UTF-16一样的2个字节呢?请解释原因。 - berimbolo
终于,一篇好文章。 - cristian9804

15

如果我可以总结一下这个帖子中我所了解到的内容:

Unicode将字符分配给序数编号(用十进制表示)。(这些数字称为代码点。)

à -> 224

UTF-8是一种编码方式,它将这些序数(以十进制形式表示)翻译成二进制表示

224 -> 11000011 10100000

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


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....

所以说我们有一个字符串:

Hello

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

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

只是一堆代码点。实际上只是数字。我们还没有说如何将其存储在内存中或表示为电子邮件消息...

这就是编码的作用。

最早的Unicode编码想法,导致了关于两个字节的谬论,嘿,让我们只是将那些数字每个存储在两个字节中。所以Hello变成了00 48 00 65 00 6C 00 6C 00 6F。

对吗?不要这么快!还可以是:

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


在 ASCII 中,字母也映射到一个代码点,而不仅仅是在 Unicode 中。 - brighty

5

它们是同一件事情,不是吗?

不,它们不是。


我认为你引用的维基百科页面的第一句话已经很好地概括了:

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

更详细地说:

  • Unicode is a standard, which defines a map from characters to numbers, the so-called code points, (like in the example below). For the full mapping, you can have a look here.

    ! -> 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在这里给出了非常好的解释和编程历史概述这里


3

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

Unicode 是用于表示许多语言中的大量字符的标准。


5
“8-bit sequences”……?可能需要更精确地说明…… - deceze
“8位序列”指的是可以以8位格式呈现。像这些,0100000111010011 1000010111100101 10100011 1000011011110001 10110001 10000010 10110001。正如您所看到的,在UTF-8中,它可以是最小1个字节,最大4个字节。 - Jin Lim
注意,当您想要使用1字节时,第一个数字是0。当您想要使用2字节时,前3个数字是110。当您想要使用3字节时,前4个数字是1110。当您想要使用4字节时,前5个数字是11110。嗯,你懂了吗? :) - Jin Lim

2

简单明了地回答:

  • Unicode是一个标准,用于表示来自许多人类语言的字符。
  • UTF-8是一种编码Unicode字符的方法。

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


这个答案真正回答了Unicode和UTF-8概念及其作用的问题。 - Joe

1

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

这里我列出了一些有用的单行命令。

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 技巧或快捷方式,请评论。
在 Bash 中,你会感谢 libiconv 和 util-linux 包中的 iconv、hexdump 和 xxd(在其他 *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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接