我对编码的理解

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

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

最近正阅读的技术书籍有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编号:

python 复制代码
# 1. 原字符串
>>> s = 'Hello 嘟嘟'

# 2. 将其以UTF-8格式进行编码(encode函数,默认参数是utf-8)
>>> b = s.encode()
>>> b
b'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的规则进行逆解。

Unicode与UTF-8之间的转换关系表

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

拼接之后的二进制码为:

perl 复制代码
# 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对象进行编码,这理解起来会简单些:

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

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

"嘟"在哪里?

五、引用链接

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

1、Unicode码表:zh.wikipedia.org/wiki/Unicod...

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

3、OpenGL的文字渲染:learnopengl-cn.readthedocs.io/zh/latest/0...

4、文字分类:zh.wikipedia.org/wiki/%E6%96...

5、UTF-8的编码方式:zh.wikipedia.org/wiki/UTF-8#...

相关推荐
Mint69 天前
深入解析Java和Go语言中String与byte数组的转换原理
java·utf-8·golang·unicode·string·rune·byte
pumpkin845143 个月前
好玩的Unicode表情
unicode
穷人小水滴4 个月前
(同步) 从 Unicode 标准提取拼音数据
unicode
hello2world4 个月前
排序探究
前端·unicode
FPGA之旅5 个月前
STM32外部Flash-----unicode字库制作基于LvglFontTool(AuroraFOC)
stm32·单片机·嵌入式硬件·unicode·字库·lvglfonttool
河豚学前端7 个月前
一个看起来只有2个字长度却有8的字符串引起的bug
正则表达式·unicode
去伪存真7 个月前
我说一个中文占2个字节,同事说UTF-8编码中,一个中文占3个字节,到底谁对谁错?
前端·unicode
酥风8 个月前
字符编码简史:从二进制到UTF-8
前端·后端·unicode
飞翔的佩奇9 个月前
IO流:java中解码和编码出现乱码说明及代码实现
java·unicode·数组·io流·编码·解码·gbk