字符编码

“手持两把锟斤拷,口中疾呼烫烫烫”

一. 乱码

在使用电脑时,你是否遇见过下面的场景:

乱码

那么,乱码是怎么来的?“锟斤拷”又是什么鬼?

二. 乱码是怎么来的?

1. ASCII

要知道,计算机可不认识什么文字,它们只认识数字
更准确地来讲,计算机只能够存储0和1的二进制数字

所以,要想让计算机显示出文字,就得让这些字符以某种方式转换为二进制数字来存储,并在需要的时候进行读取

1963年,ANSI[1]推出了ASCII[2],作为计算机及其他设备的文本字符编码标准

ASCII包含了0~9的阿拉伯数字小写英文字母大写英文字母常用的英文符号以及控制字符

ASCII一共收录了128个字符,每一个字符都有一个对应的数字,称为“码点”,ASCII字符集的码点,为0~127之间的数字,比如“A”对应码点65。

标准所支持的所有字符及其对应码点的集合,叫做“字符集”,下面就是ASCII的字符集:

ASCII

计算机可以储存码点的二进制,由于最大的码点是127,二进制的长度都会小于或等于7比特,但由于计算机一般以1字节(8比特)为基本单位进行读写。所以为了凑整,这些二进制数在储存时,会在前面补零,用固定的8比特来储存每个字符。

我们把从字符到计算机实际存储的内容之间的映射,叫做“编码

2. 其他编码

但ASCII只支持128个字符,对于其他语言就不够用了,尤其是中、日、韩等国家的文字
于是,不同国家和地区开始制定自己的编码标准…

比如我国大陆有1980年制定GB2312[3],港澳台有Big5[4]。后来还有对GB2312进行扩展的GBK[5](收录了简繁体汉字、日文、韩文等),以及现在使用的GB18030[6]

3. 乱码的由来

但标准不统一时,乱码问题也随之产生。

因为计算机内存里的同一个数字,在不同的字符集里,代表的可能是完全不同的字符。

如果你用一个GBK编码的文本编辑器,给你香港的朋友写个文档发送过去,在用大五码的文本编辑器读取后,就会变成这样:
乱码例子

三. 乱码问题的解决

正因如此,人们急需一种囊括了人类世界所有字符的字符集,让全世界的人都能够使用
于是,Unicode[7]诞生了:

UNICODE

随着版本的选代,Unicode囊括的字符越来越多,包括汉字、平片假名、藏文、阿拉伯文,甚至是古象形文字。2010年,emoji也被纳入Unicode。如今Unicode已经包含了超过十四万个字符

拓展:在Python中,可以使用ord('💩') # return 128169来查询字符对应的Unicode码点

四. Unicode的编码

需要注意的是,字符集只是字符及字符对应码点的集合,不代表字符一定会以对应码点被储存在计算机里,字符编码才是真正定义了从字符到计算机储存内容的映射。

曾经大部分的字符集,都只有一种编码方式。而超级大的字符集Unicode,却有很多种编码
比如:UTF-8UTF-16UTF-32

其中,UTF-32让每个字符都以32比特,即4字节的长度来储存,位数不够就向前补零,32比特足够表示Unicode里所有字符。固定的长度,能够帮助计算机明确每个字符的截断范围。

But,这样真的好吗?

原本只占一个字节的英文字符(ASCII),放到UTF-32里却要占四个字节,这使得相同内容的英文文本,UTF-32所占空间会是ASCII的四倍!而且英文字符的码点在Unicode里数字较小,实际上很多空间都用来放零了。

英文使用者:🤬🤬🤬

同样的,原本只占两个字节的汉字(GBK),放到UTF-32里却要占四个字节,空间多占一倍!

为了改善空间效率,UTF-8于1992年诞生了!

五. UTF-8

UTF-8是针对Unicode的可变长度编码。
不同于编码后长度因定为32比特的UTF-32,UTF-8针对不同字符,编码后的长度可以是:32比特、24比特、16比特、8比特。

可变长度编码

规则:

UTF-8规则

优点:

  1. 兼容ASCII

    Unicode前128个字符,正好也是ASCIl的字符,码点一样。而且UTF-8也会把那些字符,映射为1字节长度,和ASCII编码相同

  2. 节约空间

    UTF-8让Unicode里码点小的字符,也相应拥有更短的长度,通过前缀信息进行字符长度分辨,也解决了分割不明的问题

如今,UTF-8已经成为了最主流的编码,大部分时候它都是文本编辑器的默认选择,但有时仍然需要留意,UTF-8是否被支持或兼容,避免出现乱码。

六. “锟斤拷”

常见乱码“锟斤拷”是怎么来的?

它一般在UTF-8和中文编码的转换过程中产生。

Unicode字符集有一个特殊的替换符号:�,专门用于表示无法识别或展示的字符。
一些编辑器在通过UTF-8打开文本时,会把无法识别或展示的字符自动替换为这个替换符号,用于提示用户。
它在UTF-8编码后是这个3字节长度的二进制数字:

11101111 10111111 10111101

对应十六进制:

EF BF BD

如果用户在编辑器替换后点了保存,这个特殊符号就会被写入文件内容里。

如果有两个替换符号恰好连在一起:��

文件内容里就会有这个:

EF BF BD EF BF BD

如果这个时候,把文件再用GBK编码读取,打开后就会看到所有以前是两个替换符号的地方,现在都变成了“锟斤拷”。

因为在GBK中,每个汉字占用两个字节,储存在计算机里的EF BF对应汉字“锟”,BD EF对应汉字“斤”,BF BD对应汉字”拷“

连起来,就是”锟斤拷“


参考资料:

你懂乱码吗?锟斤拷烫烫烫(详解ASCII、Unicode、UTF-32、UTF-8编码)

锟斤拷�⊠是怎样炼成的——中文显示“⼊”门指南【柴知道】


  1. ANSI美国国家标准学会(AMERICAN NATIONAL STANDARDS INSTITUTE)成立于1918年。致力于制订统一的通用标准 ↩︎

  2. ASCII(American Standard Code for Information Interchange):美国信息交换标准代码是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符。 ↩︎

  3. GB2312是由中国国家标准总局1980年发布,1981年5月1日开始实施的一套国家标准 ↩︎

  4. Big5又称为大五码或五大码,是使用繁体中文(正体中文)社区中最常用的电脑汉字字符集标准,共收录13,060个汉字。 ↩︎

  5. GBK编码方案于1995年10月制定, 1995年12月正式发布。是在GB2312-80标准基础上的内码扩展规范,共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准。 ↩︎

  6. GB18030于2022年7月28日发布,2023年8月1日正式实施。新国标共收录汉字87887个,比上一版增加录入了1.7万余个生僻汉字,覆盖中国绝大部分人名、地名用生僻字以及文献、科技等专业领域的用字,能够满足各类使用需求。 ↩︎

  7. Unicode,统一码,也叫万国码、单一码,由统一码联盟开发,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。1990年开始研发,1994年正式发布1.0版本。 ↩︎