字符编码
“手持两把锟斤拷,口中疾呼烫烫烫”
一. 乱码
在使用电脑时,你是否遇见过下面的场景:
那么,乱码是怎么来的?“锟斤拷”又是什么鬼?
二. 乱码是怎么来的?
1. ASCII
要知道,计算机可不认识什么文字,它们只认识数字
更准确地来讲,计算机只能够存储0和1的二进制数字
所以,要想让计算机显示出文字,就得让这些字符以某种方式转换为二进制数字来存储,并在需要的时候进行读取
1963年,ANSI[1]推出了ASCII[2],作为计算机及其他设备的文本字符编码标准
ASCII包含了0~9的阿拉伯数字、小写英文字母、大写英文字母、常用的英文符号以及控制字符
ASCII一共收录了128个字符,每一个字符都有一个对应的数字,称为“码点”,ASCII字符集的码点,为0~127之间的数字,比如“A”对应码点65。
标准所支持的所有字符及其对应码点的集合,叫做“字符集”,下面就是ASCII的字符集:
计算机可以储存码点的二进制,由于最大的码点是127,二进制的长度都会小于或等于7比特,但由于计算机一般以1字节(8比特)为基本单位进行读写。所以为了凑整,这些二进制数在储存时,会在前面补零,用固定的8比特来储存每个字符。
我们把从字符到计算机实际存储的内容之间的映射,叫做“编码”
2. 其他编码
但ASCII只支持128个字符,对于其他语言就不够用了,尤其是中、日、韩等国家的文字
于是,不同国家和地区开始制定自己的编码标准…
比如我国大陆有1980年制定GB2312[3],港澳台有Big5[4]。后来还有对GB2312进行扩展的GBK[5](收录了简繁体汉字、日文、韩文等),以及现在使用的GB18030[6]。
3. 乱码的由来
但标准不统一时,乱码问题也随之产生。
因为计算机内存里的同一个数字,在不同的字符集里,代表的可能是完全不同的字符。
如果你用一个GBK编码的文本编辑器,给你香港的朋友写个文档发送过去,在用大五码的文本编辑器读取后,就会变成这样:
三. 乱码问题的解决
正因如此,人们急需一种囊括了人类世界所有字符的字符集,让全世界的人都能够使用
于是,Unicode[7]诞生了:
随着版本的选代,Unicode囊括的字符越来越多,包括汉字、平片假名、藏文、阿拉伯文,甚至是古象形文字。2010年,emoji也被纳入Unicode。如今Unicode已经包含了超过十四万个字符
拓展:在Python中,可以使用ord('💩') # return 128169
来查询字符对应的Unicode码点
四. Unicode的编码
需要注意的是,字符集只是字符及字符对应码点的集合,不代表字符一定会以对应码点被储存在计算机里,字符编码才是真正定义了从字符到计算机储存内容的映射。
曾经大部分的字符集,都只有一种编码方式。而超级大的字符集Unicode,却有很多种编码
比如:UTF-8、UTF-16、UTF-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比特。
规则:
优点:
-
兼容ASCII
Unicode前128个字符,正好也是ASCIl的字符,码点一样。而且UTF-8也会把那些字符,映射为1字节长度,和ASCII编码相同
-
节约空间
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
对应汉字”拷“
连起来,就是”锟斤拷“
参考资料:
ANSI美国国家标准学会(AMERICAN NATIONAL STANDARDS INSTITUTE)成立于1918年。致力于制订统一的通用标准 ↩︎
ASCII(American Standard Code for Information Interchange):美国信息交换标准代码是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符。 ↩︎
GB2312是由中国国家标准总局1980年发布,1981年5月1日开始实施的一套国家标准 ↩︎
Big5又称为大五码或五大码,是使用繁体中文(正体中文)社区中最常用的电脑汉字字符集标准,共收录13,060个汉字。 ↩︎
GBK编码方案于1995年10月制定, 1995年12月正式发布。是在GB2312-80标准基础上的内码扩展规范,共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准。 ↩︎
GB18030于2022年7月28日发布,2023年8月1日正式实施。新国标共收录汉字87887个,比上一版增加录入了1.7万余个生僻汉字,覆盖中国绝大部分人名、地名用生僻字以及文献、科技等专业领域的用字,能够满足各类使用需求。 ↩︎
Unicode,统一码,也叫万国码、单一码,由统一码联盟开发,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。1990年开始研发,1994年正式发布1.0版本。 ↩︎