网站首页 资讯 热点 行情 地区 推荐 民宿 酒店 家居 度假 滚动
首页 >  资讯 >  >  正文

我对编码的理解

2023-05-29 01:41:03来源:哔哩哔哩

在我之前的编程生涯中,总是对一些关键概念糊里糊涂不求甚解,比如进程、线程,比如堆、栈、编码。我对这很多概念的理解,都来自于各式样的博客。

最近几年看书多些,了解到最好的学习方式是将获得的知识以自己熟悉的模式进行整理(那许多的博客,是他人对自己知识的整理;当然,有一些文章,是将知识融会贯通后发明的“新招式”),于是逼着自己一个月整理一篇技术博客。

最近正阅读的技术书籍有Effective Python、《编码》,它们都提到编码、字符集、ASCII、Unicode、UTF-8、str、bytes……等相关关键字。


(资料图片)

在我理解“编码”时,再一次地产生晦涩感,于是4月的技术整理,是“我对编码的理解”。(我承认五一假期一直在玩耍,这篇文章,该在上周就整理完毕的。)

一、计算机展示文字的方式

这个世界,是先有文字然后才有计算机的。所以计算机展示文字(以“嘟”字举例)的方式,简单点说,可以是:将一张“嘟”字的图片放到屏幕上展示。

这句话可以拆成两个问题:

如何拿到图片?

如何在屏幕上展示图片?

“如何在屏幕上展示图片”并非理解编码的重点,我只提供两个思考方向:

一是在《编码》中看到的。视频适配板有一个字符生成器(character generator),在生产时就包含了所有的ASCII码字符的8x8像素图。每当需要展示字符(以“A”举例)时,先找到“A”的ASCII码0x41,再通过0x41找到存储在字符生成器中的像素图,再依据像素图将对应位置的像素点进行点亮。

二是来自OpenGL学习文档中的FreeType:使用矢量图像构造字形。大家感兴趣可以查看后面的引用链接。(该文档中“位图字体”与《编码》中思想一致,都是直接展示图片。)

本篇文章的重点是如何拿到图片?

二、字符集

如何拿到图片呢?

为回答这个问题,我们首先得知道“图片”是什么?

如上所说,图片是“文字”,是人类用来纪录特定事物、简化图像而成的书写符号(后文称“书写符号”为“字符”)。

全球有近200个国家,约6000种语言,人类主要使用的文字种类有:语素文字、音节文字、拼音文字等。其中以英语、汉语、印地语、西班牙语、法语、阿拉伯语、孟加拉语……等使用人数最多。

按照我从网上搜到的统计数字:全世界所有的字符数量是超过10万个的

如果计算机需要将这10万多个字符都展示出来,则相应的需要10万多张图片?

这些图片如何管理,字符“A”是排在字符“嘟”前面还是后面?中文、英文、法语、俄语甚至日语需要放在一起么?我是中国人,看不懂也用不上俄语,需要去设置去关心俄语图片么?

计算机起源于美国,最初的科学家确实是只关注英文的,于是设定了ASCII(American Standard Code for Information Interchange)标准——26个字母、阿拉伯数字,再加上些标点符号,够了!

随着计算机向全球普及,ASCII收录的字符显然是不够用的,于是各个国家出台自己的标准——比如Shift-JIS(日本工业标准)、GB2312(中国标准简体中文字符集)等——来表示本国的文字。

同一事物的标准越多,则代表没有标准;多标准一起存在,乱码处处可见。

为了统一标准,1988年,几大著名计算机公司合作研究出一种用来替代ASCII码的编码系统,取名为Unicode(统一化字符编码标准),它采用16位编码,每一个字符需要2个字节,可以表示65535个不同字符。全世界所有的人类语言,都使用同一个编码标准。(后来,Unicode再次扩容,分为17个面,即一共可以表示17 * 65535 = 1114095个字符。一般情况下只需要0号平面就够了。)

到此处,则可以回答节首的问题:如何拿到图片呢?

从Unicode中拿!

Unicode是什么呢?

Unicode是字符集,是一种标准,是收录全世界字符图片并为之配上一个编号的标准。比如字符“A”的编号是0x41,“嘟”的编号为0x561F(本文的编号,都是16进制)。

二、UTF-8是什么?

如上所说,现在我们知道了文字字符,是一个编号对应一张图片,这所有的对应关系合在一起成为了Unicode字符集。

在计算机在进行数据传输与存储时,为了节省空间,是只会传输和存储字符编号的。

如何存这个编号?是我们需要关注的重点。

对ASCII码来说,一个字符,一个字节就够了,挨着存就好:比如Hello的存储形式为48 65 6C 6F 2C

当标准切换为Unicode时,一个字符的编号需要两个字节长度来存储,以Hello加个嘟嘟举例,它的存储形式将变成00 48 00 65 00 6C 00 6F 00 2C 00 20 56 1F 56 1F(空格字符的编号为0x20)。

我们可以看到,中间是有很多空间被浪费的,很多的00不需要被存下来。

为了去掉这些额外空间,大佬们又为Unicode编码发明了UTF-8(8-bit Unicode Transformation Format),一种针对Unicode的可变长度字符编码。

具体的UTF-8规则此文不再阐述,我只在Python3中向大家展示一下如何将UTF-8编码转回Unicode编号:

# 1. 原字符串>>> s = 'Hello 嘟嘟'# 2. 将其以UTF-8格式进行编码(encode函数,默认参数是utf-8)>>> b = s.encode()>>> bb'Hello \xe5\x98\x9f\xe5\x98\x9f'# 3. 将编码出来的bytes对象以16进制展示>>> b.hex()'48656c6c6f20e5989fe5989f'# 4. 将“嘟”字的UTF-8码以二进制形式展示出来>>> dudu = '嘟'>>> [bin(byte) for byte in bytes(dudu, 'utf-8')]['0b11100101', '0b10011000', '0b10011111']

可以看到,上面实例第3点,当将Hello 嘟嘟的UTF-8展示形式以16进制展示出来时,是没有额外存储0x00的。

末尾“嘟”的UTF-8存储形式为e5989f,在我将其以二进制形式展示出来后,套入UTF-8的规则进行逆解。

由上面的表格可以看到,只需要将第1个字节的后4位0101、第2个字节的后6位011000、第3个字节的后6位011111进行拼接,即能得到Unicode的编码,见证奇迹的时刻到了……

拼接之后的二进制码为:

# 1. 将拼接好的二进制码赋值给int>>> b1 = int('01010110', 2)>>> b2 = int('00011111', 2)# 2. 将int转成16进制展示>>> hex(b1)'0x56'>>> hex(b2)'0x1f

可以看到,将“嘟”字转回为Unicode编号之后,与我们查表得来的0x561F是一致的。

所以,UTF-8是什么呢?UTF-8只是一种存储Unicode编号的方式。

三、总结

综上所述,我算是对与文字相关联的那些关键字——字符集、ASCII、Unicode、UTF-8、str、bytes——有些了解了。

文字是什么?文字是一张张图片,是人类用来纪录特定事物、简化图像而成的书写符号。

字符集是什么?字符集是一种标准,一种规范,一种协议,一种整理统计文字图片及图片所对应编号的一种集合。ASCII、Unicode、GBK等都是字符集。

UTF-8是什么?是存储Unicode字符集里面文字编号的一种字节编排方式。

str、bytes是什么?是Python3中的两种类型,可以很笼统地将str与Unicode对应,将bytes与UTF-8对应。

四、额外说说GBK

GBK,国家标准扩展。在Python3里面可以直接以gbk形式对str对象进行编码,这理解起来会简单些:

# 将str以gbk编码转为bytes>>> s = 'Hello 嘟嘟'>>> s.encode('gbk')b'Hello \xe0\xbd\xe0\xbd'

可以看到“嘟”字的存储形式为0xE0BD,占用2个字节,看下方截图,直接就找到映射关系了。

五、引用链接

下面链接中“%”符号后面跟着的两个字符,也是Unicode编号以UTF-8形式进行编码的。可以按照上面的逆解方式验证。

1、Unicode码表:https://zh.wikipedia.org/wiki/Unicode%E5%AD%97%E7%AC%A6%E5%88%97%E8%A1%A8(解出来的中文是“字符列表”)

2、GBK码表:https://toolhelper.cn/Encoding/GBK

3、OpenGL的文字渲染:https://learnopengl-cn.readthedocs.io/zh/latest/06%20In%20Practice/02%20Text%20Rendering/ 

4、文字分类:https://zh.wikipedia.org/wiki/%E6%96%87%E5%AD%97

5、UTF-8的编码方式:https://zh.wikipedia.org/wiki/UTF-8#UTF-8%E7%9A%84%E7%B7%A8%E7%A2%BC%E6%96%B9%E5%BC%8F

标签:

相关文章

[ 相关新闻 ]