基本概念
计算机处理的信息包括数值、文件、符号、语音、图形、图像等。在计算机内部各种信息都必须以数字化的二进制形式传送、存储和加工,因此,不管是什么信息都应转化为二进制形式来表示。
编码
信息从一种形式转化为另一种形式的过程,用预先规定的方法将字符(文字、数字、符号等)、图像、声音或其它对象转换成规定的电脉冲信号或二进制数字。
解码
编码的逆过程
字符集
支持的字符的集合(严格说是有序集合,但不一定是连续的),不同的字符集能够表示的字符范围不同
常见的字符集有 ASCII 字符集、ISO 8859系列字符集(ISO 8859-1~8859-16)、GB系列字符集(GB2312、GBK、GB18030)、BIG5 字符集、Unicode 字符集等。
码点(码位)
code point
(译作代码点或码点),在很多文章里也称之为码点编号、字符编号、码位或称编码位置,GB2312 中又叫区位码,是组成码空间(或代码页)的数值。
ASCII 码包含 128 个码点,范围是
0 ~ 7F(16进制)
扩展 ASCII 码包含 256 个码点,范围是
0~FF(16进制)
GB系列包含 65536 个码点,范围是
0~FFFF(16进制)
Unicode 包含 1114112 个码点,范围是
0~10FFFF(16进制)
。Unicode码空间划分为 17 个 Unicode 字符平面(1 个基本平面,16个增补平面),每个平面有 65536 个码点。
上述所说的包含的码位数只是现行标准的最大包含数,实际并未全部使用,比如 GBK 字符集只使用了 2 万多码点。
字符编码
字符编码(英语:Character encoding
),也称之为编码实现、编码方案、编码方式、编码格式、编码形式,一种规则,把字符集中的字符转化为某个指定集合(比如0和1两个数字所组成的位串集合或由0~9十个数字所组成的自然数集合)中的某一对象的规则,亦即在字符集与指定集合之间建立映射关系的规则。
字符编码还有一种含义,实际是字符的编码,GB2312 中又叫机内码,是字符通过编码规则转化后的以二进制或其他进制表示的比特流。
字符编码模型
传统字符编码模型
传统字符编码模型的字符集只有一种编码方式,比如 ASCII、 ISO 8859系列、GB系列。
基本上都是将字符集里的字符进行编号,然后该字符编号就是字符的编码(如 ASCII),不过 GB 系列字符集是个例外,字符编号对应的是区位码,而字符编码对应的是机内码,而区位码加上 160 才是机内码。
现代字符编码模型
现代字符编码模型的核心思想是创建一个能够用不同编码方式来编码的通用字符集,也就是说字符集和字符编码方式变成了一对多的关系,比如 Unicode 字符集有 UTF-8、UTF-16等多种编码方式。
现代字符编码模型分为了5个层次,并引入了更多的概念术语来描述:
抽象字符表ACR(Abstract Character Repertoire):明确字符的范围(即确定支持哪些字符)
编号字符集CCS(Coded Character Set):用数字编号表示字符(即用数字给抽象字符表ACR中的字符进行编号)
字符编码方式CEF(Character Encoding Form):将字符编号编码为逻辑上的码元序列(即逻辑字符编码)
字符编码模式CES(Character Encoding Scheme):将逻辑上的码元序列映射为物理上的字节序列(即物理字符编码)
传输编码语法TES(Transfer Encoding Syntax):将字节序列作进一步的适应性编码处理
因此现代字符编码模型中,字符编号和字符编码是不同的概念,同一个字符的字符编号是固定不变的,但是使用不同的编码方式的转化后的字符编码与编号可能是不同的(这里用了“可能”来表述,是因为也有直接将字符编号作为字符编码的编码方式,比如 Unicode 字符集中的 UTF-16 )
发展过程
从 ASCII 码到 EASCII,发展到后来的群雄并起,各国家地区拥有自己的编码格式,欧洲国家有 ISO8859 系列,中国有 GB系列,日本有 JIS,台湾有 BIG5,然而这是一个混战的年代,大家各自为政,没有统一的编码标准,交流起来极其麻烦。字符编码成为了程序员最头疼的问题之一
于是在 1991 年,国际标准化组织和统一码联盟组织各自开发了 ISO/IEC 10646(USC)和 Unicode 项目。他们两的野心都不小,一统江湖,千秋万载。不过很快双方都意识到世界上并不需要两个不兼容的字符集。于是他们就编码问题进行了一次非常友好地会晤,决定彼此把工作内容合并,项目还是独立存在,各自发布各自的标准,前提是两者必须保持兼容。不过由于 Unicode 这一名字比较好记,因而它使用更为广泛,成为了事实上的统一编码标准
各个国家和地区所独立制定的既兼容 ASCII 又互相之间不兼容的字符编码,微软统称为 ANSI 编码
。
ANSI 在不同操作系统上表现为不同的字符编码,比如在简体中文系统,默认为 GBK 编码
即使知道是ANSI编码,还需要知道这是哪一个国家或地区的才能解码;而且,同一份文本,只能采用一种 ANSI 编码方案来编码,比如,无法用同一种 ANSI 编码来表示既有汉字、又有韩文的文本。
一封电子邮件中可以将文本分割成不同的区块,在每个区块使用不同的 ANSI 编码来编码字符,并标明使用的哪种字符编码
ASCII
为了能够使用一种统一的方式表示数字字符、字母字符以及其他常用符号,所以就产生了一种国际化的标准编码方式:ASCII
。
ASCII(美国标准信息交换码)是基于拉丁字母的一套电脑编码系统,可以对现代英语和其它西欧语言进行编码,是一种单字节字符编码方案,主要用于文本数据编码,用于计算机与计算机、计算机与外设之间传递信息。
ASCII 规定使用 8 位(一个字节)二进制数码组合来表示 128 或 256 种可能的字符,所以称为单字节编码。
标准 ASCII
最高位为 0,使用 7 位二进制数码来表示所有的大小写字母、数字0-9、标点符号以及美式英语中使用的特殊控制字符。当固定最高位为0后,其余7位可以表示的有效字符为 128 = 2^7
个。标准ASCII编码与字符对照表如下图:
其中第一行表示前四位编码,第一列表示后四位编码,其组合的编码对应的符号如图中黄色部分,将其组合的结果转换为十进制数值的范围为0~127
。
0~31
及127
代表的 33 个字符是计算机的控制字符或通信专用字符32~126
代表的 95 个字符称为可显字符(其中SP表示space空格符号),即有特定的图形显示。可显字符中
48~57
表示 0~9 十个数字65~90
表示 26 个大写英文字母97~122
表示 26 个小写英文字母其它的表示一些常见标点符号和运算符等。
字符串“Hello”使用 ASCII 编码后在内存单元中的编码为 01001000 01100101 01101100 01101100 01101111
。
由于 ASCII 码通用性较为广泛,所以我们将 ASCII 单字节编码的字符称为 ASCII 字符。
扩展 EASCII
由于标准的ASCII码只能表示128个符号,远远不能满足现实中的符号要求,所以后来就将标准 ASCII 码的最高位也用作编码位,这样就又多出了 128 个符号,这些符号称为 EASCII 码,其能够对大部分的西欧符号进行表示。
EASCII 已很少使用
ISO 8859系列
ISO 8859,全称ISO/IEC 8859,是国际标准化组织(ISO)及国际电工委员会(IEC)联合制定的一系列 8 位元字符集的标准,现时定义了15个字符集。
ISO 8859 系列字符集也是将标准 ASCII 码的最高位用作编码位的。
ISO/IEC 8859-1 (Latin-1) - 西欧语言
ISO/IEC 8859-2 (Latin-2) - 中欧语言
ISO/IEC 8859-3 (Latin-3) - 南欧语言。世界语也可用此字符集显示。
ISO/IEC 8859-4 (Latin-4) - 北欧语言
ISO/IEC 8859-5 (Cyrillic) - 斯拉夫语言
ISO/IEC 8859-6 (Arabic) - 阿拉伯语
ISO/IEC 8859-7 (Greek) - 希腊语
ISO/IEC 8859-8 (Hebrew) - 希伯来语(视觉顺序)
ISO 8859-8-I - 希伯来语(逻辑顺序)
ISO/IEC 8859-9(Latin-5 或 Turkish)- 它把Latin-1的冰岛语字母换走,加入土耳其语字母。
ISO/IEC 8859-10(Latin-6 或 Nordic)- 北日耳曼语支,用来代替Latin-4。
ISO/IEC 8859-11 (Thai) - 泰语,从泰国的 TIS620 标准字集演化而来。
ISO/IEC 8859-13(Latin-7 或 Baltic Rim)- 波罗的语族
ISO/IEC 8859-14(Latin-8 或 Celtic)- 凯尔特语族
ISO/IEC 8859-15 (Latin-9) - 西欧语言,加入Latin-1欠缺的芬兰语字母和大写法语重音字母,以及欧元(€)符号。
ISO/IEC 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。
GB 系列
汉字的特点是象形文字,单字单音,而且数量非常庞大,字形复杂,要在计算机中以二进制序列的方式表示一个汉字,远远要比 ASCII 码复杂。所以规定汉字在输入、输出、存储和处理过程中所使用的汉字编码均不相同。
常用的 GB 系列汉字编码方案有:GB2312
、GBK
、GB18030
等。
根据应用目的的不同,汉字编码又分为区位码、国标码、内码、外码和字形码几种方式,下文将以 GB2312
为例来说明不同方式之间的区别与联系。
区位码
整个 GB2312 字符集分成 94 个区,每区有 94 个位,每个区位上只有一个字符,即每区含有94个汉字或符号,用所在的区和位来对字符进行编码(实际上就是字符编号、码点编号),因此称为区位码(或许叫“区位号”更为恰当)。
GB2312 将包括汉字在内的所有字符编入一个 94*94 的二维表,行就是“区”、列就是“位”,每个字符由区、位唯一定位,其对应的区、位编号合并就是区位码。
01~09区(682个):特殊符号、数字、英文字符、制表符等,包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母等在内的682个全角字符;
10~15区:空区,留待扩展;
16~55区(3755个):常用汉字(也称一级汉字),按拼音排序;
56~87区(3008个):非常用汉字(也称二级汉字),按部首/笔画排序;
88~94区:空区,留待扩展。
例如:
“啊”字是 GB2312 之中的第一个汉字,在
16区1位
,它的区位码就是16 01
“万”字在
45区82位
,它的区位码是45 82
交换码(国标码)
国标码(又称为交换码)规定表示汉字的范围为(0010 0001,0010 0001) ~ (0111 1110,0111 1110)
,十进制为 (33,33) ~ (126,126)
将“区码”和“位码”分别加上 32 (十六进制为 20H
或 0x20
,后缀 H 或前缀 0x
都可表示十六进制)即为国标码。
例如:
“啊”字的国标码十进制为
(16+32,01+32) = (48,33)
,十六进制为:(4D,72),二进制为:(0011 0000,0010 0001)“万”字的国标码十进制为
(45+32,82+32) = (77,114)
,十六进制为:(4D,72),二进制为:(0100 1101,0111 0010)。
内码(机内码)
国标码中的每个字节的最高位都从 0 换成 1,即相当于每个字节都再加上 128 (十六进制为 80H
;二进制为 1000 0000
),从而得到国标码的“机内码”表示,简称“内码”。
例如:
“啊”字的机内码十进制为
(48+128,33+128) = (176,161)
,十六进制为:(B0,A1)“万”字的机内码十进制为
(77+128,114+128) = (205,242)
,十六进制为:(CD,F2)
外码(输入码、输入法编码)
外码也叫输入码、输入法编码,是用来将汉字输入到计算机中的一组键盘符号,是作为汉字输入用的编码。
英文字母只有 26 个,可以把所有的字符都放到键盘上,而使用这种办法把所有的汉字都放到键盘上是不可能的。所以汉字系统需要有自己的输入码体系,使汉字与键盘能建立起对应关系。
目前常用的汉字外码分为以下几类:
数字编码,比如区位码;
拼音编码,比如全拼、双拼、自然码等;
字形编码,比如五笔、表形码、郑码等。
汉字外码往往会出现重码:同一个汉字外码对应于多个汉字,比如使用拼音作为外码时(使用拼音输入法输入汉字时),重码现象是相当普遍的。
当出现重码时,往往需要附加选择编号以具体确定所要输入的汉字,这种情况下,可认为外码实际上相当于隐式地包括了选择编号在内。
字形码(字型码、字模码、输出码)
字形码,又称为字型码、字模码、输出码,属于点阵代码的一种。
为了将汉字在显示器或打印机上输出,把汉字按图形符号设计成点阵图,就得到了相应的点阵代码(字形码)。
也就是用 0、1 表示汉字的字形,将汉字放入 n行*n列 的正方形(即点阵)内,该正方形共有 n^2 个小方格,每个小方格用一位二进制数表示,凡是笔划经过的方格其值为 1,未经过的方格其值为 0。
显示一个汉字一般采用16×16点阵或24×24点阵或48×48点阵。已知汉字点阵的大小,可以计算出存储一个汉字所需占用的字节空间。
比如,用 16×16 点阵表示一个汉字,就是将每个汉字用 16 行,每行 16 个点表示,一个点需要 1 位二进制数,16 个点需用 16 位二进制数(即 2 个字节),所以需要16行×2字节/行=32字节,即以 16×16 点阵来表示一个汉字,字形码需要 32 字节。
因此,字节数=点阵行数×(点阵列数/8)。
为了将汉字的字形显示输出或打印输出,汉字信息处理系统还需要配有汉字字形库,也称字模库,简称字库,它集中存储了汉字的字形信息。
字库按输出方式可分为显示字库和打印字库。用于显示输出的字库叫显示字库,工作时需调入内存。用于打印输出的字库叫打印字库,工作时无需调入内存。
字库按存储方式也可分为软字库和硬字库。软字库以字体文件(即字形文件)的形式存放在硬盘上,现多用这种方式。硬字库则将字库固化在一个单独的存储芯片中,再和其它必要的器件组成接口卡,插接在计算机上,通常称为汉卡。这种方式现已淘汰。
各码之间的转换及关系
事实上,英文字符的输入、处理和显示过程大致上也差不多,只不过英文字符不需要输入码(即外码),直接在键盘上输入对应的英文字母即可。
GB2312 对 ASCII 中存在的英文字母、数字和符号采用双字节进行了重新编码,为了区分,GB2312 中的这些字符称为全角字符,ASCII 中的这些字符称为半角字符
Unicode
Unicode 编码空间
目前,Unicode 码空间划分为 17 个 Unicode 字符平面(1 个基本平面,16个增补平面),每个平面有 65536 个码点,将来根据需要,还可扩展为更多平面。
其中第 0 个平面 BMP
(Basic Multilingual Plane,也称为基本多语言平面、基本多文种平面,往往简称为基本平面、平面0),基本涵盖了当今世界上正在使用中的常用字符。我们平常用到的字符,一般都是位于 BMP
平面上的。
BMP
平面以外其他的增补平面 SP
(Supplementary Plane,也称为辅助平面)要么用来表示一些非常特殊的字符(比如不常用的象形文字、远古时期的文字等),且多半只有专家在历史和科学领域里才会用到它们;要么被留作扩展之用。目前Unicode字符集的17个平面中尚有大量编号空间未被使用。
Unicode 的码点范围为 U+0000
到U+10FFFF
,17 个平面的码点可表示为从 U+xx0000
到 U+xxFFFF
,其中 xx
表示十六进制值从 00
到 10
Unicode 编码方式
Unicode字符集的三种编码方式:UTF-8、UTF-16、UTF-32。
UTF-8 、UTF-16、UTF-32 等属于不同的编码方式,只是它们编码的字符都属于 Unicode 字符集。而 GB2312 和 GBK 除了编码方式不同之外,它们的字符集也不相同,后者是前者的扩展
UTF-8 应该是目前应用最广泛的一种 Unicode 编码方式(但不是最早面世的,UTF-16 要早于 UTF-8 面世)
码元(Code Unit),代码单元、编码单元,可理解为字符编码方式CEF(Character Encoding Form)对码点值进行编码处理时作为一个整体来看待的最小基本单元
本文很多内容参考 笨笨阿林的知乎专栏:刨根问底学编程,想要更深入的了解字符编码的知识可以移步过去