UTF-8

本文介绍了UTF-8编码的工作原理,如何通过编码规则与Unicode一一对应,以及如何解决计算机识别字节长度不一的字符的问题。UTF-8通过变长字节解决了Unicode编码的储存空间浪费,确保了字符的兼容性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章假设你已经熟知计算机的基本单位( 1 字节 = 8 位二进制 = 2 位十六进制 ),且对 Unicode 有清楚的理解。

描述

网络上已经有很多关于 UTF-8 的描述版本了,这里尝试从另一个角度对 UTF-8 进行描述。

Unicode 使用「 1个16进制的字节+2个16进制字节 」(从 000010 FFFF )的编码方式与全球所有语言的字符进行对应,但这会大大提高浪费计算机储存空间的概率。比如很多英文字母的 Unicode 码是 00xx ,单单一个字母就浪费了2个单位( 00 )的储存位,这样一篇英文文章至少会浪费一半的储存空间。为了合理利用计算储存空间,UTF-8 应运而生。
之前的计算机只能死脑筋地认为 i 个字节表示一个字符,这会导致新字符的兼容性问题(例如后来加入新的字符需要 i+1 个字节表示)以及前文提及的储存空间浪费问题。
UTF-8 通过一定的规则,使得字符转译生成的编码的长度不再固定。除了本身的 Unicode 编码之外,每个字符还会嵌入自己的编码长度信息。UTF-8 的引入增加了字符的编码长度的可拓展性,允许将来新的字符的引入,也同时避免了储存空间的浪费。

编码规则

UTF-8 的编码规则只有2条:

  1. 对于单字节的 UTF-8 编码(二进制),字节的第 1 位设为 0,后面 7 位为这个字符的 Unicode 码。因此对于英文字母,UTF-8 编码和 ASCII 码是相同的。
  2. 对于 n 字节(n > 1)的 UTF-8 编码(二进制),第 1 个字节(二进制)的前 n 位都设为1,第 n+1 位设为 0,后面字节的前 2 位一律设为10。剩下的没有提及的二进制位,全部为这个字符的 Unicode 码。

上面的规则描述可用下面的表格来诠释(字母 x 表示可填编码的位):

字节数UTF-8 编码结构(二进制)可填写的编码范围(二进制)
10xxxxxxx(0)000 0000 ~ (0)111 1111
2110xxxxx 10xxxxxx(0)000 0000 0000 ~ (0)111 1111 1111
31110xxxx 10xxxxxx 10xxxxxx0000 0000 0000 0000 ~ 1111 1111 1111 1111
411110xxx 10xxxxxx 10xxxxxx 10xxxxxx(000)0 0000 0000 0000 0000 0000 ~ (000)1 1111 1111 1111 1111 1111

这样的规则解决了2个问题:计算机如何识别字节长度不一的字符、UTF-8 如何与 Unicode 一一对应

计算机如何识别字节长度不一的字符

UTF-8 是允许有不同字节数的字符存在的,那么计算机编译器是如何从类似111001101011010110110111111010011001100010010100 这样的0/1编码中识别出一个个字符的?
计算机编译器在编译第 1 个字节时,判断这个字节的第 1 位是 0 还是 1 : 若是 0,则这个字节就表示一个字符;若是 1 ,则计算从这个 1 开始(包含)到 0 结束,有 n 个 1,那么从这个字节开始(包含)的 n 个字节就表示一个字符。
当识别出第 1 个字符后,以此类推,继而判断第 2 个字符的字节数,识别出第 2 个字符;再识别第 3 个字符……直到最后一个字符,这样就实现了所有字符的识别(解码)过程。
比如上面的 1110010110111100100000000101000001100001011100100111010001111001 可以分割为6个字符:
| 11100101 10111100 10000000 | 01010000 | 01100001 | 01110010 | 01110100 | 01111001 |

UTF-8 如何与 Unicode 一一对应

根据 UTF-8 的规则,字符在有限的字节编码中,抛去标示声明字节的编码位,剩下的就是可编写的——见上面表格“UTF-8 编码结构(二进制)”中字母“x”表示的二进制位,就是留给 Unicode 码填入的,这就实现 UTF-8 与 Unicode 的一一对应了。
但是这些可编写的二进制位的编码范围是有限的,如 1 个字节的 UTF-8 编码可编写 27 2 7 (即128) 个 Unicode 码,即可编写从 0000007F 的 Unicode 码。
下表总结了不同字节数 UTF-8 码可编码的 Unicode 范围(字母 x 表示可用编码的位):

UTF-8 编码结构(二进制)可编写 Unicode 位数(二进制)可编写 Unicode 编码范围(十六进制)
0xxxxxxx0~7位0000 0000 ~ 0000 007F
110xxxxx 10xxxxxx8~11位0000 0080 ~ 0000 07FF
1110xxxx 10xxxxxx 10xxxxxx12~16位0000 0800 ~ 0000 FFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx17~21位0001 0000 ~ 0010 FFFF

以中文字符 为例,将其 Unicode 码 6D77 编译为 UTF-8 码步骤:
1. 将十六进制 6D77 转换为二进制 1101101 01110111,位数是15位;
2. 根据规则,3 个字节的 UTF-8 码可编译 12~16 位 Unicode 码(见上表),最适合编译当前字符;
3. 将 1101101 01110111 依次填入 3 字节的 UTF-8 码可编辑位(多余的最高位补上 0 ),得到 11100110 10110101 10110111 ,这个就是 UTF-8 码了;
4. 如果愿意,可以将其转换为 16 进制 E6 B5 B7
值得注意的是,虽然超过 3 个字节的 UTF-8 码的可编写位数也能够填入 15 位的 1101101 01110111、多余的高位补上 0 ,但这样做编译器不会同等识别出来。
笔者猜测是因为这样不利于节约储存空间,且已经有最优解——采用 3 个字节的 UTF-8 码编译,所以 UTF-8 干脆不做重复的字符编码了。因此在 UTF-8 中,推荐以最短字节数表示字符,不允许冗余的字节。这也是上面表格中“可编写 Unicode 编码范围(十六进制)”的起点不和 UTF-8 可编辑位范围一致的原因( 1 字节除外)。
从这点来看,UTF-8 的变长字节优势凸显,但也存在部分编码留白的现象。

参考:字符编码详解

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值