char类型只包含1个字节的存储空间,而1个字节最多能表达256种不同的值。如果只表达英文字符及其标点符号,1个字节足够。但其它文字,比如中文,其“字符”有数万之多。在编码其它语言文字时,可能会使用到不同的多字节编码方案。
本文引用自作者编写的下述图书; 本文允许以个人学习、教学等目的引用、讲授或转载,但需要注明原作者"海洋饼干叔
叔";本文不允许以纸质及电子出版为目的进行抄摘或改编。
1.《Python编程基础及应用》,陈波,刘慧君,高等教育出版社。免费授课视频 Python编程基础及应用
2.《Python编程基础及应用实验教程》, 陈波,熊心志,张全和,刘慧君,赵恒军,高等教育出版社Python编程基础及应用实验教程
3. 《简明C及C++语言教程》,陈波,待出版书稿。免费授课视频
计算机是二进制的,它的文件和内存严格地说,只能存储和处理比特流,每个比特对应二进制的1位。由于这些二进制位按8位一组被分隔成字节,我们也可以认为计算机只能存储和处理字节流,用大白话说就是一个接一个的字节。
所以,当我们把由多个字符构成的文章存储到计算机里的时候,存入到内存或者硬盘的并不是文字本身,而是这些文字字符的对应编码。我们以ASCII码为例为说明相关过程。ASCII码是所谓美国标准信息交换代码,它用一个字节来表示一个字符。一个字节有8位,共256种组合,用来表示英文字符、数字及标点绰绰有余。
ASCII码的完整码表请见: 美国信息交换标准交换代码表 - Python,C/C++ Club (codelearn.club)
为便于讨论,我们在Windows下用记事本编辑了如下内容的文件,并保存为hw.txt。

查看hw.txt的文件属性,可见该文件事实上占据了11个字节的空间。

回顾hw.txt的文件内容,其中的”Hello World”连同其中的空格一起,正好11个字符。

注意:000000 000013表示序号,不是文件的构成内容。
在Windows命令行下,使用如上图所述的od命令可以查看文件hw.txt的原始内容,其内的11个字节的值用16进制表示出来就是:
48 65 6C 6C 6F 20 57 6F 72 6C 64
其中,48在ASCII码里对应’H’,65对应’e’,20对应空格,64对应’d’…
将文本存储至计算机内时,需要先把文本中的字符逐一转换成对应的编码,这一过程被称为”文本编码“。比如,文本”Hello World”被编码成字节流”48 65 6C 6C 6F 20 57 6F 72 6C 64“。
对于Windows记事本而言,文件hw.txt就是包含11个字节的字节流,需要将这11个字节按照码表映射至文本,再显示出来,这一过程被称为”文本解码“。比如,字节流”48 65 6C 6C 6F 20 57 6F 72 6C 64“被转换成”Hello World”。
同一文字符号,在不同的编码方案内,其对应的字节或字节序列很可能不同。
粗略地理解,Unicode就是一张表,这张表将世界上”所有“的文字字符,甚至一些表情符号(emoji表情)对应成互不相同的整数。Unicode®自发明以来,一直在不断修订中,以收录”新“的语言符号。至2022年9月13日止,Unicode® 15.0.0总共收录了149,186个字符。Unicode的表太长,下表给出了其中的几个示例。
表1 Unicode编码示例
| 符号 | 编码(整数,16进制) | 说明 |
|---|---|---|
| B | 0042 | 英文字符 |
| Ê | 00CA | |
| ڣ | 06A3 | 阿拉伯语字符 |
| ॵ | 0975 | 未知语言字符 |
| ᅰ | 1170 | 韩语符号 |
| ∫ | 222B | 数学符号之积分 |
| ⛺ | 26FA | 帐篷 |
| ざ | 3056 | 日语假名 |
| 人 | 4EBA | 汉字 |
完整的Unicode®表格可以通过下述链接查询:https://unicode-table.com/en/
Unicode® 15.0.0的标准原文见:https://www.unicode.org/versions/Unicode15.0.0/
UTF-8可以认为是Unicode的一种实施方案,通过UTF-8可以把一个字符对应的Unicode编码(一个整数)转换成一个字节序列,这个字节序列可能包含1个、2个、3个或者4个字节。
表2 Unicode编码与UTF-8的转换关系
| Unicode编码(16进制) | UTF-8字节序列 | 说明 |
|---|---|---|
| 000000 ~ 00007F | 0xxxxxxx | 单字节, 含7个x |
| 000080 ~ 0007FF | 110xxxxx 10xxxxxx | 2字节,含11个x |
| 000800 ~ 00FFFF | 1110xxxx 10xxxxxx 10xxxxxx | 3字节, 含16个x |
| 010000 ~ 10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 4字节, 含21个x |
我们使用Windows记事本编辑了一个”多国语言文件”,其中包含英文字母“B”,阿拉伯母字母“ڣ”,以及汉语的“人”,然后按UTF-8编码保存为文件mixed.txt。

注意保存时选择UTF-8编码。

查看mixed.txt文件属性,发现其尺寸为6个字节。

再次使用od命令查看文件mixed.txt的原始内容,结果如下:

注意:000000, 000006是序号信息,不是文件的构成部分。
其中的”42 DA A3 E4 BA BA”与前述分析完全相同。编码程序完全按照上述UTF-8规则将文字”Bڣ人”转换为字节序列”42 DA A3 E4 BA BA”,然后把这个字节序列共6个字节写入文件mixed.txt。
当我们用记事本将文件mixed.txt中的原始内容读出时,其就是由6个字节构成的字节序列:42 DA A3 E4 BA BA₁₆ 。 作为一个文字编辑软件,记事本需要通过UTF-8解码将上述字节序列查表转换成”Bڣ人”,然后再将其显示出来。字符的显示过程涉及绘图、字体等内容,此处略去不提。
我们可以假想出记事本软件内部的解码程序的执行过程:
从上述解码过程,读者不难看出,UTF-8的编码是没有歧义的,即特定的字节序列与特定的文本内容一一对应。在以汉字为主的文档里,使用UTF-8编码,一个汉字需要3个字节来表达,而使用GB2312-80,一个汉字只需要2个字节来表达。相较于后者,UTF-8编码的文 档需要占据更多的存储空间以及网络带宽。
作者觉得没什么意思,UTF-8现在是主流,读者在安装Linux,设计网页时,如果选用UTF-8编码,是最安全的。
事实上,除了UTF-8,GB2312-80,ASCII,世界还有很多很多乱七八糟的编码方案…

读者或许在程序开发的过程中看到过如上图所示的乱码现象。所谓乱码,本质是文本编码与文本解码发生了错位,如果把一个GB2312-80编码的文档按照UTF-8来进行解码,自然会发生错误,形成所谓“乱码”。
为了帮助更多的年轻朋友们学好编程,作者在B站上开了两门免费的网课,一门零基础讲Python,一门零基础C和C++一起学,拿走不谢!
如果你觉得纸质书看起来更顺手,目前Python有两本,C和C++在出版过程中。