Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

聊一聊UTF-8编码 #220

Open
soapgu opened this issue Aug 16, 2023 · 0 comments
Open

聊一聊UTF-8编码 #220

soapgu opened this issue Aug 16, 2023 · 0 comments
Labels

Comments

@soapgu
Copy link
Owner

soapgu commented Aug 16, 2023

  • 前言

不怕出丑其实在进公司面试的时候,回答编码的问题就没回答好。以后成了我的一块“心病”。对于编码总多了一份“敬畏之心”。
正好前面工作中又碰到了,索性以UTF-8编码为线索,一并来捋一捋编码的前世今生。

  • 起源 —— ASCII

如果说起源可以从密码学聊起,就是通过一种编码来准确表达一种文字

图片

比如凯撒密码就是所有的字母进行3的移位,但是这本质还是文字的加密暂时和我们计算机的编码不一样,不过“转换”的性质是一样的。

首先计算机的“语言”是什么

0和1才是计算机能认识的语言

计算机的标准用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为字节

于是这种128位的编码就产生了

遇上0×10, 终端就换行;

遇上0×07, 终端就向人们嘟嘟叫;

遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。

于是以上就把这些0×20以下的字节状态称为”控制码”

他们又把所有的空
格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号。

图片

上图初中高中的计算机课应该都见过

这样一共编了128个字符。于是大家都把这个方案叫做 ANSI 的”Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。

  • 不为人知的扩展ASCII码

ASCII很好,但是很快不够用了,新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状等等。一个byte不是正好可以表达256位嘛,ASCII只编了128位,不是后面还有128位嘛,所以后面的128位进行了扩展编码全用上,这128个扩充字符是由IBM制定的,因此又叫IBM扩展字符集。但是根本不能满足人们对字符的需求,这个扩展编码并没有什么卵用!所以才没人知道的原因吧!

  • 著名的GB2312编码

说到编码怎么能跳开GB2312!虽然现在已经越用越少,但是当年在windows 98/XP时代绝对是统治地位存在。
即使是扩展ASCII也远远不能满足我们中国人的需求!我们有6000多个常用汉字需要表达,远远不够。
我们智慧的中国人民之跳出单子节byte思维,直接把两个字符拿来一起用。
规定

  • 一个小于127的字符的意义与原来相同,就是ASCII编码,也就是著名的半角字符
  • 但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。这也就是全角字符了

当初古早输入法都是有全角/半角的切换设置就是这么来的。

好了最后引用官方的正式名称

《信息交换用汉字编码字符集》是由中国国家标准总局1980年发布,1981年5月1日开始实施的一套国家标准,标准号是GB/T 2312-1980。

  • GB2312编码带来的“刻板印象”

和所有的刻板印象一样

  • 玩游戏就会影响学习
  • 味精有毒
  • 火腿肠的肉都是垃圾肉

这些我一律统称呼为“刻板印象”
然后然后出现了这个贻害无穷大家都觉得对的公理(思想钢印)

英文占一个字符,中文占两个字符

这句话有局限性,只在GB2312的世界正确

  • 详细了解下2个字节的GB2312怎么会不够那

历史惊人的相似,和ASCII一样,GB2312编码还是不能满足聪明的中国人的字符的需求,我们很快就就发现有许多人的人名没有办法在这里打出来

理论上2个字节可以表达65,536种组合,虽然我们汉字常用字只有6000多,怎么看上去余量绰绰有余,为啥会不够?这也是我在了解过程中心中的疑问

原来我们GB2312的编码是这样的,分成了一个94*94的矩阵,落在矩阵里面里面就是字符。
每一个字符就是由区码和位码组成,形象成二维坐标更好理解,就相当于横坐标(x)和纵坐标(y)

其中区码的分布如下

  • 01-09区为特殊符号。
  • 16-55区为一级汉字,按拼音排序。
  • 56-87区为二级汉字,按部首/笔画排序。
  • 10-15区及88-94区则未有编码。

可以看出GB2312并不是把所有码位都“用足”的,我们粗略统计,就是吧不编码的区也用上,“满打满算”GB2312的字符集表达也就94*94=8836,并不算多!

  • 不为人知的GBK编码

和扩展ASCII一样,GBK也是一样的命运。
虽然它又多扩展了近20000个新的汉字,继续大大利用了2个字节的空间
他的编码结构是兼容GB2312
这个图大概了解下不做进一步深入

图片

  • 不同编码标准的痛苦

GBK的出现暂时解决了字符集不够的问题,但是新的痛点又出现了。

玩过KOEI游戏的人都知道,所有的曹操都会变成“变巨”。需要使用一款神奇的翻译软件——南极星可以正常显示。
由于港台地区的繁体字用的是另外一套编码——BIG5。

好嘛,现在编码届已经乱成一锅粥了,除了ASCII是被普遍接受以外,接下来的扩展字符集就完全是各玩各的。

  • 大一统的Unicode编码来了

终于有人看不下去了。一个叫 ISO(国际标准化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。

在这种语言环境下,不会再有语言的编码冲突,在同屏下,可以显示任何语言的内容,这就是统一码的最大好处。就是将世界上所有的文字用2个字节统一进行编码。那样,像这样统一编码,2个字节就已经足够容纳世界上所有语言的大部分文字了

现在用的是UCS-2,即2个字节编码,而UCS-4是为了防止将来2个字节不够用才开发的。
编码空间 0-0x10FFFF
编码分布简单介绍,Unicode和GB2312多区位码分布有异曲同工之处
每个平面有2^16=65536个码位。统一码计划使用了17个平面,一共有17*65536=1114112个码位。
其中平面15和平面16上只是定义了两个各占65534个码位的专用区(Private Use Area),分别是0xF0000-0xFFFFD和0x100000-0x10FFFD。这些位置是给自定义的
平面0有部分码位被专用,其中有一部分叫代理区(Surrogate)的特殊区域,这是为了UTF-16做准备的

  • Unicode编码的阻力

听上去很美好,但是一度很难推广,为啥。
举例说明

I 0049
t 0074
' 0027
s 0073
  0020
知 77e5
乎 4e4e
日 65e5
报 62a5

计算机只懂二进制,因此,严格按照unicode的方式(UCS-2),应该这样存储:

I 00000000 01001001
t 00000000 01110100
' 00000000 00100111
s 00000000 01110011
  00000000 00100000
知 01110111 11100101
乎 01001110 01001110
日 01100101 11100101
报 01100010 10100101

看到问题没有,英文和中文是一样的大小,英文的第一个字节是0,是大大的浪费,这样文本凭空大了一倍。

  • 聊回正题——UTF-8的诞生

重复重复

UTF-8不是字符集!
UTF-8不是字符集!
UTF-8不是字符集!

字符集的配方还是Unicode!
字符集的配方还是Unicode!
字符集的配方还是Unicode!

UTF-8是编码规则!
UTF-8是编码规则!
UTF-8是编码规则!

UTF-8也并不孤单,他还有兄弟UTF-16和UTF-32。最后只有UTF-8火了。
其实顾名思义,UTF-8是8位一组编码,UTF-16和UTF-32就是分别16位、32位一组了。

  • UTF-8的具体编码规则

如果用文字表达简直又臭又长,用表格来表示清晰多了

字节 格式 实际编码位 码点范围
1字节 0xxxxxxx 7 0 ~ 127
2字节 110xxxxx 10xxxxxx 11 128 ~ 2047
3字节 1110xxxx 10xxxxxx 10xxxxxx 16 2048 ~ 65535
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 21 65536 ~ 2097151

英文还是一个字节。
但是中文不一定是2个字节了,3个4个字节也是可能的

  • ASCII字符(包括英文字母、数字和常用符号)只需要1个字节。
  • 带有附加符号的拉丁文、希腊文、西里尔字母等某些欧洲语言字符使用2个字节。
  • 大多数非ASCII字符,包括大部分中文字符,使用3个字节。
  • 极少数使用的字符,如在 Unicode 高位区域的字符,使用4个字节。

中文在UTF-8占用空间大也是一个令人诟病的地方,不过为啥还是UTF-8一枝独秀?

  • UTF-16的具体编码规则一

UTF-16就是16个位,2个字节,范围为0~0xFFFF。刚刚好一个平面,问题是Unicode有17个平面。所以一个单位是编不满的,有点尴尬。这样就造成UTF-16也是一个可变编码,16位或者是32位的

  1. 范围 <0x10000,就是BMP(Basic Multilingual Plane)
    直接使用16位直接编码

2.超过U>0x10000

同样的Unicode目前最大的编码是0x10FFFF
我们用二进制来表示更加直观

0001 0000 1111 1111 1111 1111

就是啥意思,就是最左边的0001,只用到最后一位。
实际上就用了 1+ 4*5 一共是21位

4个byte的位是32位空间也是没有全部占满!

接下来骚操作的编码来了

U - 0x10000 = 20位二进制

由于最大编码是0x10FFFF,0x10FFFF-0x10000最大值正好是0xFFFFF,正好20位不多不少!

然后把20位一分两半,yyyy yyyy yyxx xxxx xxxx
最终编码 110110yyyyyyyyyy 110111xxxxxxxxxx

你以为结束了?没有!

  • UTF-16的具体编码规则二

文重点讲解的是 UTF-16 编码格式字节数组的转化。UTF-16 顾名思义,就是用两个字节表示一个字符。那么用两个字节表示必然存在字节序的问题,即大端小端的问题。下面就来讲讲 UTF-16BE、UTF-16LE、UTF-16 三者之间的区别吧。
UTF-16BE,其后缀是 BE 即 big-endian,大端的意思。大端就是将高位的字节放在低地址表示。
UTF-16LE,其后缀是 LE 即 little-endian,小端的意思。小端就是将高位的字节放在高地址表示。
UTF-16,没有指定后缀,即不知道其是大小端,所以其开始的两个字节表示该字节数组是大端还是小端。即FE FF表示大端,FF FE表示小端。

表格示意

内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78
  • UTF-32编码

占用4个字节,由于空间大大冗余,不需要做任何转换计算,变成了定长编码。除了空间太大,一样有小端模式问题

  • 小结

相比UTF-16、UTF-32的缺点,UTF-8对中文占用空间不友好的问题变得能忍受了,他容错高(8位一个单元),便于传输(大小端问题),字符空间足够大(理论上4字节长度还能扩)、完美兼容ASCII编码。

综上现在UTF-8是最流行的编码,目前几乎所有的网站都支持UTF-8编码,几乎再也看不到网页会有乱码了!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant