字符编码(一:术语及字符编码由来)面对字符编码,经过各种资料与维基查询,想自己写一篇笔记总结一下,写到一半发现了一位大佬
面对字符编码,经过各种资料与维基查询,想自己写一篇笔记总结一下,写到一半发现了一位大佬(博客:笨笨阿林)已经做了比较好的总结 作者原文。所以本人这里直接摘抄转载过来(也结合了其他文章内容),会有一些细节优化补充等,以便理清思路、逐字细读及随时查阅。链接方面有很多都是维基百科,需要科学才能打开。
争取一文系统了解 “字符编码”(因上传字符数量限制,分为五篇),不再苦恼。
全文链接:
字符编码(一:术语及字符编码由来) 字符编码(二:简体汉字编码与 ANSI 编码 ) 字符编码(三:Unicode 编码系统与字节序) 字符编码(四:UTF 系列编码详解) 字符编码(五:网络传输编码 Base64、百分号编码)1.0 前言
字符编码是计算机世界里最基础、最重要的一个主题之一。不过,在计算机教材中却往往浮光掠影般地草草带过,甚至连一本专门进行深入介绍的著作都找不到。而在编程实践中,如果不发扬死磕到底的精神将字符编码问题的来龙去脉、前世今生彻底搞清楚,那么它终将会像幽灵一样挥之不去,导致时不时地被各种与字符编码相关的 “灵异” 事件折磨得死去活来。
字符编码的基础性、重要性,主要体现在它涉及面广。向下涉及到计算机的底层技术,甚至是硬件实现;向上几乎跟所有的操作系统、编程语言、应用程序都密切相关。
类似于字符编码这样基础、重要、应用广泛而又特别容易让人困惑的主题还有字节序(即大小端表示)、正则表达式以及浮点数实现、日期时间处理等等。其中,字节序、正则表达式跟字符编码的关系又密切相关,尤其是字节序,直接影响字符编码的字节序列。而由于正则表达式主要用于在字符串中查找、提取字符或子字符串,要想真正理解正则表达式,也离不开对字符编码的深入理解。
1.1 关键术语解释
要想真正搞明白字符编码问题,必须得从计算机的基本概念 - 位、字节、字符集、字符编码等等开始,再结合不同的系统环境与编程环境,进行具体分析。
个人加入了进制相关内容,原文不包括。
比特、位
位,即比特(Bit),亦称二进制位、比特位、位元,指二进制数中的一位,是计算机中信息表示的最小单位。
Bit 是 Binary digit(二进制数位)的混成词,由数学家 John Wilder Tukey 提出(可能是 1946 年提出,但有资料称 1943 年就提出了)。这个术语第一次被正式使用,是在香农著名的论文《通信的数学理论》(A Mathematical Theory of Communication)第 1 页中。位(比特)习惯上以小写字母 b 表示,如 8 比特可表示为 8b 。
每个比特有 0 和 1 两个可能的值,除了代表数值本身之外,还可代表:
数值的正、负; 两种状态,如电灯的开、关,某根导线上电流的有、无,等等; 抽象逻辑上的是、否,或者说真、假。字节、8 位组
在计算机中,通常都会使用一连串的位(即比特),称之为位串(即比特串,bit string)。很显然,计算机系统都不会让你使用任意长度的位串,而是使用某个特定长度的位串。
一些常见的位串长度形式具有约定好的名称,如,半字节(nibble)代表四个位的组合,字节(byte)代表 8 个位的组合;还有字(word)、双字(Double word,简写为 Dword)、四字(Quad word,简写为Qword)、十字节(Ten byte,简写为 Tbyte)等等。
字节(byte),港澳台又称为位元组,音译为 “拜特”(但很少使用这个译名),是计算机中计量存储容量和传输容量的一种基本计量单位,是由连续的、固定数量的位(即比特)所组成的位串(即比特串),一般由 8 个位组成,即 1 byte = 8 bit。字节习惯上用大写的 B 表示,所以也可以表示为 1 B = 8 b。
现代个人计算机(PC)的存储器编址,一般是以字节为单位的,称之为按字节编址,因此字节一般也是存储器的最小存取单位以及处理器的最小寻址单位(也有按位寻址、按字寻址等等,但在个人计算机上应用不普遍,这里不讨论)。
字节作为存储器的最小存取单位以及处理器的最小寻址单位这一重要特点,跟字符编码的关系极为密切,比如,码元的单字节与多字节、字节序的大端序与小端序等,都与以字节为基础的基本数据类型密切相关(详见后文介绍)
习惯上,按照下面的图来排列一个字节上的各个位的顺序,即按照从右到左的顺序,依次为最低位(第 0 位)到最高位(第 7 位):

注意,字节不一定非得是 8 位,以前也有过 4 位、6 位、7 位、12 位或 18 位作为一个字节的标准,比如 IBM 701(36 位字长,18 位为一字节)、IBM 702(7 位字长,7 位为一字节)、CDC 6600(60 位字长,12 位为一字节 byte)等。只是现代计算机的事实标准就是用 8 位来代表一个字节(最终形成这一事实标准除了历史原因和商业原因之外,最重要的原因应该是由于二进制的特性:2 的次方计算更方便快捷)。
正是因为这个原因,在很多较为严谨的技术规格文献中,为了避免产生歧义,更倾向于使用 8 位组(Octet)而不是字节(Byte)这个术语来强调 8 比特串。
不过,由于大众基本上都将字节理解为 8 比特的 8 位组,因此一般文章中如果未作特别说明,基本上都将 8 位组直接称之为字节,或者说,字节一般指的是 8 位组。
ps:后文中频繁出现高位、最高位、高字节;低位、最低位、低字节等术语,其中 “高”、“低” 是个相对概念。在以人类读写习惯中,二进制数,从左往右数,左边就是高,右边就是低。详细解释见后文《 1.7 字节序》
字、字长
虽然字节是大多数现代计算机的最小存储单位和传输单位,但并不代表它是计算机可以最高效地处理的数据单位。
一般来说,计算机可以最高效地处理的数据大小,应该与其字的字长相同,这就涉及到了字及字长的概念。
字:在计算机中,一串比特位(即位串、比特串)是作为一个整体来处理或运算的,这串比特位称为一个计算机字,简称字。字通常分为若干个字节。 字长:即字的长度,是指计算机的每个字所包含的位数。字长决定了 CPU 一次操作所处理的实际比特位数量的多少。字长由 CPU 对外数据通路的数据总线宽度决定。计算机处理数据的速率,显然和它一次能加工的位数以及进行运算的快慢有关。如果一台计算机的字长是另一台计算机的两倍,若两台计算机的速度相同,在相同的时间内,前者能做的工作一般是后者的两倍。因此,字长与计算机的功能和用途有很大的关系,是计算机的一个重要技术指标。
在目前来讲,桌面平台的处理器字长正处于从 32 位向 64 位过渡的时期,嵌入式设备基本稳定在 32 位,而在某些专业领域(如高端显卡),处理器字长早已经达到了 64 位乃至更多的 128 位。
进制表示法
字符编码本质是字符与二进制之间的映射(除了 ASCII 码,其他都并不是直接映射,需要进行额外转换计算),但是二进制对于人类过于不直观,记不住,就有了其他更利于人类理解记忆的进制表示法。非计算机专业的同学有必要先了解常用进制(二进制、八进制、十六进制)本身含义和他们在不同环境、系统、语言中的表示方法,他们是有很大区别的。
进制也就是进位计数制,是人为定义的带进位的计数方法(有不带进位的计数方法,比如原始的结绳计数法,唱票时常用的 “正” 字计数法,以及类似的 tally mark 计数)。 对于任何一种进制 --- X 进制,就表示每一位置上的数运算时都是逢 X 进一位。 十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,x 进制就是逢 x 进位。
二进制二进制(binary)在数学和数字电路中指以 2 为基数的记数系统,以 2 为基数代表系统是二进位制的。这一系统中,通常用两个不同的符号 0(代表零)和1(代表一)来表示 。数字电子电路中,逻辑门的实现直接应用了二进制,因此现代的计算机和依赖计算机的设备里都用到二进制。每个数字称为一个比特。
二进制已经是计算机所用的最基本语言了,任何地方都用 0 和 1 表示。
计算机采用二进制原因:
首先,二进位计数制仅用两个数码:0 和 1,所以,任何具有二个不同稳定状态的元件都可用来表示数的某一位。而在实际上具有两种明显稳定状态的元件很多。例如,氖灯的 "亮" 和 "熄" ;开关的”开“和”关“; 电压的”高“和”低“、”正“和”负“;纸带上的”有孔“和“无孔”,电路中的”有信号“和”无信号“, 磁性材料的南极和北极等等,不胜枚举。 利用这些截然不同的状态来代表数字,是很容易实现的。不仅如此,更重要的是两种截然不同的状态不单有量上的差别,而且是有质上的不同。这样就能大大提高机器的抗干扰能力,提高可靠性。而要找出一个能表示多于二种状态而且简单可靠的器件,就困难得多了。
其次,二进位计数制的四则运算规则十分简单。而且四则运算最后都可归结为加法运算和移位,这样,电子计算机中的运算器线路也变得十分简单了。不仅如此,线路简化了,速度也就可以提高。这也是十进位计数制所不能相比的。
第三,在电子计算机中采用二进制表示数可以节省设备。可 以从理论上证明,用三进位制最省设备,其次就是二进位制。但由于二进位制有包括三进位制在内的其他进位制所没有的优点,所以大多数电子计算机还是采用二进制。此外,由于二进制中只用二个符号 “ 0” 和“1”,因而可用布尔代数来分析和综合机器中的逻辑线路。 这为设计电子计算机线路提供了一个很有用的工具。
八进制八进制,Octal,缩写 OCT 或 O,一种以8为基数的计数法,采用 0,1,2,3,4,5,6,7 八个数字,逢八进 1。一些编程语言中常常以数字 0 开始表明该数字是八进制。八进制的数和二进制数可以按位对应(八进制一位对应二进制三位),因此常应用在计算机语言中。
八进制表示法在计算机系统中很常见,因此,我们有时能看到人们使用八进制表示法。由于十六进制一位可以对应 4 位二进制数字,用十六进制来表示二进制较为方便。因此,八进制的应用不如十六进制。有一些程序设计语言提供了使用八进制符号来表示数字的能力,而且还是有一些比较古老的 Unix 应用在使用八进制。
在编程语言中,八进制文字通常与不同的前缀,包括数字 0、字母 o 或 q、0o 组合,或符号 & 或美元。
例如,文字 73(八进制)可以表示为 073, o73, q73, 0o73, 73, @73, &73, $73 or 73o 在各种语言中。
新的语言( ECMAScrip 等)已经放弃前缀 0 的写法,因为字母 o 和 数字 0 太容易被误认了,容易混乱。一般使用前缀0o。
前缀 环境 0o ECMAScript6、C、Python3.0、Ruby 等等 十六进制十六进制(简写为hex或下标 16)在数学中是一种逢 16 进 1 的进位制。一般用数字 0 到 9 和字母 A 到 F 表示,其中:A - F 相当于十进制的 10 - 15,这些称作十六进制数字。
例如十进制数 57,在二进制写作111001,在 16 进制写作 39。
现在的 16 进制则普遍应用在计算机领域,这是因为将 4 个位元(Bit)化成单独的 16 进制数字不太困难。1 个字节(Byte)可以表示成 2 个连续的 16 进制数字。可是,这种混合表示法容易令人混淆,因此需要一些字首、字尾或下标来显示。
不同电脑系统、编程语言对于十六进制数值有不同的表示方式,其中最常用(或常见)表示十六进制数值的方式是将 0x 加在数字前,或在数字后加上小字 16。
环境 格式 备注 URL %hex XML、XHTML &#xhex HTML、CSS #hex 用于表示颜色 Unicode U+hex 表示字符编码 UTF-8 +hex Unicode 的子集, UTF-16 uhex Unicode 的子集,16 位通用字符名 UTF-32 Uhex Unicode 的子集,32 位通用字符名 MIME =hex Modula-2 #hex Smalltalk,ALGOL 68 16rhex Common Lisp #xhex 或 #16rhex IPv6 8个 hex 用 : 分隔 Intel 汇编语言 hexh 其他汇编器 $hex 在 VB 等等 &HhexC 语言、C++、Shell、Python、Java 语言及其他相近的语言使用字首 “0x”,例如 “0x5A3”。开头的 “0” 令解析器更易辨认数,而 “x” 则代表十六进制(就如 “O” 代表八进制)。在 “0x” 中的 “x” 可以大写或小写。
进制之间的转换自行了解。
编码、解码
编码(Encode),是信息从一种形式转换为另一种形式的过程,比如用预先规定的方法将字符(文字、数字、符号等)、图像、声音或其它对象转换成规定的电脉冲信号或二进制数字。
解码(Decode),为编码的逆过程。

字符集
字符集(Character Set、Charset),字面上的理解就是字符的集合,是一个自然语言文字系统支持的所有字符的集合。字符是各种文字和符号的总称,包括文字、数字、字母、音节、标点符号、图形符号等。
例如 ASCII 字符集,定义了 128 个字符;GB2312 字符集定义了 7445 个字符。而字符集准确地来说,指的是已编号的字符的有序集合(但不一定是连续的,比如 EASCII,后文有详细介绍)。
常见字符集有 ASCII 字符集、ISO 8859 系列字符集(ISO 8859-1~8859-16)、GB 系列字符集(GB2312、GBK、GB18030)、BIG5 字符集、Unicode 字符集等。

图中所示微软在 GB2312 的基础上扩展制订了 GBK (Guo-Biao Kuozhan),然后 GBK 才成为 “国家标准”(也有说GBK不是国家标准,只是 “技术规范指导性文件”);但网上也有资料说是先有 GBK(由全国信息技术标准化技术委员会 1995 年 12 月 1 日制订),然后微软才在其内部所用的 CP936 代码页中以 GBK 为基础进行了扩展,亦即Windows 系统中的 CP936 代码页实际上是 GBK 编码方案的一个实现(原作者更倾向于这后一种说法)
字符编码
字符编码(Character Encoding),也称字集码,是把字符集中的字符按一定方式编码为某指定合集中的某一对象(例如:比特模式、自然数序列、8 位组或者电脉冲)的过程,,以便文本在计算机中存储和通过通信网络的传递。
亦即在字符集与指定集合两者之间建立一个对应关系(即映射关系)的过程。这是信息处理的一项基础技术。
因此,通常以字符集来定义字符,以计算机为基础的信息处理系统则利用电子元件(即硬件)的不同状态的组合来表示、存储和处理字符。
电子元件(即硬件)的不同状态(一般为断开和闭合两种状态)的组合能代表数字系统中的数字(比如断开和闭合代表二进制中的 0 和 1),因此字符编码的过程也就可以理解为将字符转换映射为计算机可以接受的二进制数字的过程,这样才便于字符在计算机中表示、存储、处理和传输(包括在网络中传输)。
常见的例子是将常用的拉丁字母表编码成摩斯电码和 ASCII 码。其中,ASCII 将字母、数字和其它符号进行编号,并且在计算机中直接用 7 比特的二进制数字来表示这个编号。通常会额外地在最高位(即首位)再增加一个扩充的比特位 “0”,以便于计算机系统刚好以 1 个字节(8 比特位)的方式来进行处理、存储和传输。
传统字符编码模型
字符编码模型(Character Encoding Model),是反映字符编码系统的结构特点和各构成部分相互关系的模型框架。
由于历史的原因,早期一般认为字符集和字符编码是同义词,并不需要进行严格区分。因此在像 ASCII 这样的简单字符集为代表的传统字符编码模型中,这两个概念的含义几乎是等同的。
因为在传统字符编码模型中,基本上都是将字符集里的字符进行编号(字符编号转化为二进制数后一般不超过一个字节),然后该字符编号就是字符的编码(这句话后文有详细解释)。

现代字符编码模型
由统一码(Unicode)和通用字符集(UCS,或称ISO/IEC 10646)为代表的现代字符编码模型则没有直接采用 ASCII 这样的简单字符集的编码思路(即传统字符编码模型),而是采用了一个全新的编码思路。
这个全新的编码思路将字符集与字符编码的概念更为细致地分解为了以下几个方面:
有哪些字符; 这些字符的编号是什么; 这些编号如何编码成一系列逻辑层面有限大小的数字,即码元序列; 这些逻辑层面的码元序列如何转换为(即映射为)物理层面的字节序列(即字节流); 在某些特殊的传输环境中(比如 Email 中),再进一步将字节序列进行适应性编码处理。这几个方面作为一个整体,于是构成了现代字符编码模型 - 维基百科。
现代字符编码模型之所以要分解为这么几个方面,其核心思想是创建一个能够用不同方式来编码的通用字符集。注意这里的关键词:“不同方式” 与 “通用”。
这意味着,同一个字符集,可以通用于不同的编码方式;也就是说,可以采用不同的编码方式来对同一个字符集进行编码。字符集与编码方式之间的关系可以是一对多的关系。
更进一步而言,在传统字符编码模型中,字符编码方式与字符集是紧密结合在一起的;而在现代字符编码模型中,字符编码方式与字符集脱钩了。用软件工程的专业术语来说,就是将之前紧密耦合在一起的字符编码方式与字符集解耦了。
因此,为了正确地表示这个现代字符编码模型,需要采用更多比 “字符集” 和 “字符编码” 更为精确的概念术语来描述。
在 Unicode Technical Report (UTR统一码技术报告) #17《UNICODE CHARACTER ENCODING MODEL》中,现代字符编码模型分为了 5 个层次,并引入了更多的概念术语来描述:
第 1 层,抽象字符表 ACR(Abstract Character Repertoire):明确字符的范围(即确定支持哪些字符); 第 2 层,编号字符集 CCS(Coded Character Set):用数字编号表示字符(即用数字给抽象字符表 ACR 中的字符进行编号);ps:这就是现代字符编码模型中的 “字符集” 第 3 层,字符编码方式 CEF(Character Encoding Form):将字符编号编码为逻辑上的码元序列(即逻辑字符编码);ps:UTF 系列 第 4 层,字符编码模式 CES(Character Encoding Scheme):将逻辑上的码元序列映射为物理上的字节序列(即物理字符编码);ps:本地计算机中运行的字节流 第 5 层,传输编码语法 TES(Transfer Encoding Syntax):将字节序列作进一步的适应性编码处理。ps:为网络传输,将本地字节流做特殊编码,如 Base64,百分号编码(即 URL 编码 )。ps:下文即是现代字符编码模型的 5 个层次详细讲解,个人建议初次阅读先跳过这一段,先看后文再回头理解,贯穿全文思路。
1 抽象字符表 ACR抽象字符表 ACR 是一个编码系统支持的所有抽象字符的集合,可以简单理解为无序的字符集合,用于确定字符的范围,即要支持哪些字符。
抽象字符表 ACR 的一个重要特点是字符的无序性,即其中的字符并没有编排数字顺序,当然也就没有数字编号。
“抽象”字符不具有某种特定的字形,不应与具有某种特定字形的“具体”字符混淆。
字符表可以是封闭的(即字符范围是固定的),即除非创建一个新的标准,否则不允许添加新的字符,比如 ASCII 字符表和 ISO/IEC 8859 系列都是这样的例子;字符表也可以是开放的(即字符范围是不固定的),即允许不断添加新的字符,比如 Unicode 字符表和一定程度上 Code Page 代码页(代码页后文会有详细解释)是这方面的例子。
2 编号字符集 CCS一般将 “Coded Character Set” 翻译为 “编码字符集” 或 “已编码字符集”,但这里的 “编码” 二字容易导致与后文的 “编码方式” 及 “编码模式” 中的 “编码” 二字混淆,带来理解上的困扰,因此觉得翻译为 “编号字符集” 为宜。
前面讲了,抽象字符表里的字符是没有编排顺序的,但无序的抽象字符表只能判断某个字符是否属于某个字符表,却无法方便地引用、指称该字符表中的某个特定字符。
为了更方便地引用、指称字符表中的字符,就必须为抽象字符表中的每个字符进行编号。
所谓字符编号,就是将抽象字符表 ACR 中的每个抽象字符(简称字符)表示为 1 个非负整数 N 或者映射到 1 个坐标(非负整数值对 x,y ),也就是将抽象字符的集合映射到一个非负整数或非负整数值对的集合,映射的结果就是编号字符集 CCS。因此,字符的编号也就是字符的非负整数代码。
例如,在一个给定的抽象字符表中,表示大写拉丁字母 “A” 的字符被赋予非负整数 65、字符 “B” 是 66,如此继续下去。
由此产生了编号空间( Code Space,一般翻译为代码空间、码空间、码点空间 )的概念:根据抽象字符表中抽象字符的数目,可以设定一个字符编号的上限值(该上限值往往设定为大于抽象字符表中的字符总数),从 0 到该上限值之间的非负整数范围就称之为编号空间。
编号空间的描述:
可以用一对非负整数来描述,例如:GB2312的汉字编号空间是94 x 94;
也可以用一个非负整数来描述,例如:ISO-8859-1的编号空间是256;
也可以用字符的存储单元尺寸来描述,例如:ISO-8859-1是一个8比特的编号空间(2^8=256);
还可以用其子集来描述,如行、列、面(Plane平面、层面)等等。
编号空间中的一个位置( Position )称为码点( Code Point 代码点 )或码位 ( Code Position 代码位)。一个字符占用的码点所在的坐标(非负整数值对)或所代表的非负整数,就是该字符的编号,又称之为码点值(即码点编号)。
不过,**严格来讲,字符编号并不完全等同于码点编号(即码点值)。**因为事实上,由于某些特殊的原因,编号字符集 CCS 里的码点数量要大于抽象字符表 ACR 中的字符数量。
在编号字符集中,除了字符码点之外,还存在着非字符码点和保留码点,所以字符编号不如码点编号准确;但若对字符码点的码点编号称之为字符编号,倒也更为直接。
在 Unicode 编码方案中,字符码点又称之为 Unicode 标量值( Unicode Scalar Value,感觉不如字符码点更为直观),非字符码点和保留码点详见后文介绍。
**注意,**经常直接以“码点”指代“码点值”——当说到“码点”的时候,有可能是在说编号空间(即码点空间)中的一个位置,即码点;也有可能实际是在说某个码点的码点值,即码点编号。其具体含义需根据上下文而定。
这种类似的指代非常普遍,又比如“字符集”、“字符编号”和“字符编码”这三个概念就经常相互指代。以“码点”指代“码点值”,根据上下文,倒也还不难理解;但“字符集”、“字符编号”和“字符编码”三者也经常相互指代,虽然有其令人无奈的历史原因,但目前的实际情况所导致的结果却是使人迷惑、让人抓狂!
因此,所谓编号字符集,可简单理解为就是把抽象字符进行逐个编号或者说逐个映射为码点值(即码点编号)后所得到的结果。
编号字符集 CCS 常简称为字符集。
注意不要将编号字符集 CCS 和抽象字符表 ACR 相混淆。多个不同的编号字符集 CCS 可以表示同一个抽象字符表 ACR;换言之,同一个抽象字符表 ACR,可以按不同的编号规则被编号为多个不同的编号字符集 CCS。

在 Unicode 标准中,一个单个的抽象字符,既有可能与多个码点对应(为了与其它标准兼容,比如码点编号为 U+51C9 与 U+F979 的这两个码点实际上是同一个字符“凉”,这是为了兼容韩国字符集标准 KS X 1001:1998,具体可参看Unicode 的官方文档),也有可能使用一个由多个码点所组成的码点序列表示(为了表示由基本字符与组合字符组合在一起所组成的字符,比如 à,由码点编号为 U+0061 的基本字符字母 “ a ” 和码点编号为 U+0300 的组合字符读音符号 “̀ ”组成);同时,也并非每一个码点都对应于一个字符,也存在着非字符码点或保留码点。详见后文解释。
特别注意:虽然“编号”与后文某种字符编码方式 CEF 中的“编码”(即码元序列,解释详见后文)以及某种字符编码模式 CES 中的“编码”(即字节序列,解释详见后文)存在着对应的关系,但**“编号”与“编码”是截然不同的两个概念**。
对字符编号的过程——即确定字符码点值的过程,跟计算机还没有直接关系,可认为是一个纯数学的问题,因为只是将字符与编号(即码点值、码点编号)一一对应起来,根本就还没有涉及到编码算法的问题(即还没有涉及到:根据指定的字符编码方式 CEF 对编号进行编码以形成码元序列,以及根据指定的字符编码模式 CES 对码元序列进一步编码以形成字节序列)。
但这两个概念经常被混用,实实在在是让人困惑的源头,这是很令人无奈与遗憾的现实。
3 字符编码方式 CEF在讲抽象字符表 ACR 时说过,不同于传统的封闭的 ASCII 字符表其字符数是固定的,Unicode 字符表是一个现代的开放的字符表,其字符数是不固定的,未来可能有更多的字符加入进来(比如很多 Emoji 表情符就被源源不断地加入进来)。因此,Unicode 编号字符集所需要的码点数量,必然是会不断增加的,相对来说是无限的。
但计算机所能表示的整数范围却是相对有限的。比如,一个无符号单字节整型数( unsigned char, uint8 )能够表示的编号只有 0~0xFF,共 256 个;一个无符号双字节短整型数( unsigned short, uint16 )能够表示的编号只有 0~0xFFFF,共 65536 个;而一个无符号四字节长整型数( unsigned long, uint32 )能表示的编号只有 0~0xFFFFFFFF,共 4294967296 个。
那么问题来了:
一方面,怎么通过相对有限的整型数来高可扩展地、高可适应地应对未来相对无限增长的字符数量呢?是用多个单字节整型数来间接表示,还是用一个足够大的多字节整型数来直接表示呢?
另一方面,ASCII 字符编码作为最早出现、已被广泛应用的编码方案,完全不兼容显然不明智,那么是直接兼容,还是间接兼容呢?
这两方面的问题需要一个综合解决方案,这就是字符编码方式 CEF( Character Encoding Form )。
字符编码方式 CEF,是将编号字符集里字符的码点值(即码点编号、字符编号)转换成或者说编码成有限比特长度的编码值(即字符编码)。该编码值实际上是码元( Code Unit 代码单元、编码单元)的序列( Code Unit Sequence )。
那什么是码元呢?为什么要引入码元这个概念呢?字符集里的字**符编号(即码点值、码点编号)又是如何转换为计算机中的字符编码(即码元序列)**的呢?别急,这里先记下这个概念,暂不深究,后文有详细解释。
字符编码方式 CEF 也被称为 “存储格式”( Storage Format )。不过,将 CEF 称之为存储格式实际上并不合理,因为 CEF 还只是逻辑层面上的、与特定的计算机系统平台无关的编码方式,尚未涉及到物理层面上的、与特定的计算机系统平台相关的存储方式(第 4 层才涉及到)。
在 ASCII 这样传统的、简单的字符编码系统中,并没有也不需要区分字符编号与字符编码,可认为字符编号就是字符编码,字符编号与字符编码之间是一个直接映射的关系。
而在 Unicode 这样现代的、复杂的字符编码系统中,则必须区分字符编号与字符编码,字符编号不一定等于字符编码,字符编号与字符编码之间不一定是一个直接映射的关系,比如 UTF-8、UTF-16 为间接映射,而 UTF-32 则为直接映射。
UTF-8、UTF-16 与 UTF-32 等就是 Unicode 字符集(即编号字符集)常用的字符编码方式 CEF。( UTF-8、UTF-16 与 UTF-32 后文各有详细介绍)
很多文章中,将 Unicode 与各 UTF( Unicode/UCS Transformation Format,包括 UTF-8、UTF-16 与 UTF-32 等)之间的关系表述为:Unicode 为标准规范、各 UTF 为编码实现。
不严格地来讲,也可以勉强这么认为。不过,这种表述毕竟不够严谨,而且无助于理解 Unicode 标准(即 Unicode 编码方案、Unicode 编码系统)、Unicode 字符集与各 UTF 字符编码方式这三者之间更为深入细致的关系。
注:从现代字符编码模型的角度来看,Unicode 编码标准、Unicode 编码方案、Unicode 编码系统基本上为同义词,是包括了抽象字符表 ACR、编号字符集 CCS、字符编码方式 CEF以及下面要讲到的字符编码模式 CES甚至传输编码语法 TES五个层面在内的一整套标准方案系统,并不特指其中的某一层。

一般将 “ Character Encoding Scheme ” 翻译为 “字符编码方案”,但习惯上通常将 “字符编码标准” 或 “字符编码系统” 称之为 “字符编码方案”,为避免引起理解上的困扰,应译作 “字符编码模式” 为宜。
字符编码模式 CES,也称作“序列化格式”( Serialization Format ),指的是将字符编号进行编码之后的码元序列映射为**字节序列(即字节流)**后的形式,以便经过编码后的字符能在计算机中进行处理、存储和传输。
如果说将编号字符集的码点值(即字符编号)映射为(即编码为)码元序列的过程属于跟特定的计算机系统平台无关的逻辑意义上的编码过程(该编码过程即第3层的字符编码方式 CEF 的编码过程),那么将码元序列映射为字节序列的过程就属于跟特定的计算机系统平台相关的物理意义上的编码过程(该编码过程即第 4 层的字符编码模式 CES 的编码过程)。
由于硬件平台与操作系统设计上的历史原因,对于 UTF-16、UTF-32 等采用多字节码元的编码方式而言,必须使用一个原先称之为零宽度不中断空格(ZERO WIDTH NO-BREAK SPACE)的字符( Unicode 字符编号为 0xFEFF )来指定字节序( Byte-Order 字节顺序、位元组顺序,或称为 Endianness 端序)是大端序还是小端序,计算机才能够正确地进行处理、存储和传输。(什么是字节序以及大端序、小端序,解释详见后文)
不过,对于 UTF-8 这种采用单字节码元的编码方式来说,并不存在字节序问题,不需要指明字节序。因此,在各种计算机系统平台中,UTF-8 编码的码元序列与字节序列都是相同的。(为什么 UTF-8 不存在字节序问题,解释详见后文)
注意,由于字符编码方式 CEF与字符编码模式 CES中都有“编码”二字,因此,通常所说的动词编码( Encode )有可能指的是通过字符编码方式 CEF 将编号字符集CCS 的字符编号转变为码元序列;也有可能指的是通过字符编码模式CES 将字符编码方式 CEF 的码元序列转变为字节序列。
动词**解码( Decode )**则反过来,当然也同样存在两种可能。
而通常所说的名词编码( Encoding )也就相应地有可能指的是码元序列,也有可能指的是字节序列。(当然,在很多文章中,名词编码往往实际上表述的是字符的编号,既非码元序列,亦非字节序列,虽然有其历史原因,但从现代字符编码模型的角度来看,这应算作是一种错误表述,或至少可认为是表述不够严谨。)
因此,必须根据上下文语境来具体理解。
对于程序员而言,通过字符编码方式 CEF 编码后所形成的码元序列,更多的是一种逻辑意义上的中间编码(即编码中介,属于从字符编号到字节序列的中间状态,作为将字符编号转换为字节序列的中介而存在),不是平时直接“打交道”的对象。
而通过字符编码模式 CES 将码元序列进一步编码后所形成的字节序列,才是平时直接“打交道”最多的物理意义上的最终编码(虽然事实上还有第 5 层的传输编码语法 TES 所形成的编码,但这种编码毕竟仅用于某些特殊的传输环境,平时 “打交道” 的机会较少)。
5 传输编码语法 TES由于历史的原因,在某些特殊的传输环境中,需要对上一层次的字符编码模式 CES 所提供的字节序列(字节流)作进一步的适应性编码处理。一般包括两种:
一种是把字节序列映射到一套更受限制的值域内,以满足传输环境的限制,例如用于 Email 传输的 Base64 编码或者 quoted-printable 编码(可打印字符引用编码),都是把 8 位的字节映射为 7 位长的数据。
注:Email 协议设计为仅能传输 7 位的 ASCII 字符;从一个 8 位字节的角度来看,ASCII 字符的首位始终为 0,所以除去首位的 0,实际有效的位数为 7 位,或许最初是出于节约传输流量的考虑,Email 协议被设计为了仅能传输 7 位的 ASCII 字符。
另一种是压缩字节序列的值,如 LZW 或者进程长度编码等无损压缩技术。
小结总结一下现代字符编码模型:

对于 Unicode 这样的现代字符编码系统来说,同一个字符因多个不同的字符编码方式 CEF(如 UTF-8、UTF-16、UTF-32等)而具有多个不同的码元序列(Code Unit Sequence),同一个码元序列因两个不同的字符编码模式 CES(大端序模式、小端序模式)而又可能具有两个不同的字节序列(Byte Sequence)。
但这些不同的码元序列也好,字节序列也好,只要表示的是同一个字符,所对应的**码点值(即码点编号、字符编号)**一般都是相同的(在 Unicode 标准中,为了与其它标准兼容,有少数字符可能与多个码点对应)。
1.2 字符编码的由来
计算机这个东西,最初是美国人发明的。计算机一开始发明出来时是用来解决数字计算问题的,后来人们发现,计算机还可以做更多的事,例如文本处理等。
但计算机它只 “认识” 010110111000… 这样由 0 和 1 两个数字组成的二进制数字,这是因为计算机的底层硬件实现就是用电路的断开和闭合两种状态来表示 0 和 1 两个数字的。因此,计算机只可以直接存储和处理二进制数字。
为了在计算机上也能表示、存储和处理像文字、符号等等之类的字符,就必须将这些字符转换成二进制数字。
当然,肯定不是我们想怎么转换就怎么转换,否则就会造成同一段二进制数字在不同计算机上显示出来的字符不一样的情况,因此必须得定一个统一的标准进行转换。
于是就设计出了进行这种转换的标准 --- 字符编码标准。
演变故事
预先了解下字符编码方式的演变(与正文无关,可以跳过看下一节)(原文地址):
作为一个可以计算、存储、通信的复杂玩意 --- 计算机,最最基本的功能,应该是能读懂人类让它干的事情。所以,他们得构造一个计算机能用的语言,这个语言计算机能看懂,人也能看懂,这样才能交流嘛。
二进制与 Hex计算机所用的语言是什么呢?这个语言非常简单,只有 0 和 1 两种二进制数表示(因为计算机用高电平和低电平分别表示 1 和 0)。0 代表否,1 代表是。通过 0 和 1 的各种组合,以及 0 和 1 之间的各种运算(位运算),计算机就能进行理解、分析这个世界,并帮助人类完成工作了。
但是 0 和 1 太简单了,简单到任何一个简单的数字都可能用一长串 0 和 1 来表示。举了例子,如果让计算机记住 1000 (十进制)这个数,计算机就要记住 1111101000 (二进制)这么长一串数字。计算机倒是好记,但是人类记不住啊… 有没有一种方法,能够让计算机表示的数据短一点,好记一点呢?
人类习惯于使用十进制( Dev ),毕竟人类有是个手指,十个一进位,挺好的!但是让计算机用十进制吧,这不现实,因为十进制转化为二进制,太麻烦了(十进制转化二进制的方法,可以查阅相关资料)。
所以,为了兼顾两者,8 进制和 16 进制都是可以接受的。8 进制的话,只使用 0 ~ 7 这 8 个数字就好了。十六进制(也就是 Hex 编码 ),用 0 - 9 表示前 10 个数,后面的用 A、B、C、D、E、F 表示,不区分大小写。其中大部分都是使用十六进制居多。
Hex 编码原理Hex 编码原理是:把一长串二进制数每 4 个分一组,如果位数不够就在高位补 0。4 位数字一共只有 16 种情况,分别用 0-9,A-F(不分大小写)表示这 16 种情况。编码表类似这样:
编码(二进制) 字符(十六进制) 编码(二进制) 字符(十六进制) 0 0 1000 8 1 1 1001 9 10 2 1010 A 11 3 1011 B 100 4 1100 C 101 5 1101 D 110 6 1110 E 111 7 1111 F 可读性更强的 ASCII 编码Hex 编码虽然好,但有个问题:从计算机上打开个文件,满眼的十六进制数,很头大啊… 十六进制还是不太好表示文本。能不能创建一种方法,能表示键盘打出来的全部英文字符、符号呢?键盘打不出来的字符,比如什么回车啦,占位啦,用特殊的符号表示。这样一来,打开一个文件,满眼英文,岂不是很爽快~
美国作为计算机的始祖国家,自然要推出一个这样的标准代码表。这就是美国信息交换标准代码,简称 ASCII 码表(后有多种 ASCII 扩展,也称 ASCII 超集、ANSI 编码等,详细见后文)。这个码表包括了数字、英文大小写、符号、以及各种各样的转义字符,可以包含英文所用的全部功能。很快地,ASCII 码成为了国际标准,现在大家知道的其他编码形式,都是与 ASCII 码兼容的。
别的语言怎么办? Unicode 和 UTF-8ASCII 码表一出来,英语国家开心了…其他国家的脑袋疼了… 带注音的符号怎么办?日语韩语怎么办?最为博大精深的中文怎么办… 于是,各个国家也推出了本国语言的编码表(也就是后文中会详说的 ANSI 编码,这是个统称,等同 ASCII 超集、扩展 ASCII 等概念)。但是,为了能在计算机系统中通用,这些编码表基本都与 ASCII 码兼容。
最为知名的就是 UTF-8( Unicode 字符集的其中一个实现) 了。这个编码又称为万国码,顾名思义,就是支持包括中文简体、中文繁体、日语、韩语等各种语言的编码。
用一种编码打开另一种编码会怎样?既然每个国家都有自己的编码表了,问题也就来了。现在都国际化了,我要用一个支持本国语言的编码系统,打开另一个编码系统编码的文本,会出现什么情况呢?这就是乱码了… 更为严重的是,随着互联网的出现,各个国家的电脑都需要通信,而通信的一种方式就是使用 URL 地址。每个国家都希望把这个地址写成自己国家的语言。但这会导致其他国家根本没法访问地址,因为打不出这个字符嘛。所以,人类迫切需要一种中间编码形式,既能够兼容 ASCII 码,又能够把任意一种编码形式转换成只使用可读字符( ASCII 码中的可打印字符,详细见后文)就能表示的编码。其中一种编码形式,就是 Base64 编码。
字符编码方式的演变故事结束,接下来回到原文。
EBCDIC 码
最开始设计出来的字符编码标准是 EBCDIC 编码标准。EBCDIC,是 Extended Binary Coded Decimal Interchange Code(即扩展二进制编码的十进制交换码)的缩写。
EBCDIC 码是由国际商用机器公司(即 IBM)为大型机操作系统而开发设计的,于 1963 年 - 1964 年间推出。
在 EBCDIC 码中,英文字母不是连续排列的,中间出现多次断续,这为撰写程序的人带来了一些困难。
因此,在后来 IBM 的个人计算机和工作站操作系统中并没有采用 EBCDIC 码,而是采用了晚于 EBCDIC 码推出、且后来成为了英文字符编码工业标准的 ASCII 编码方案。

ASCII 码
ASCII 码(American Standard Code for Information Interchange 美国信息交换标准码),由美国国家标准学会ANSI(American National Standard Institute)于 1963 年公布,1968 年正式制定,也可称之为:US-ASCII 码或者基础 ASCII 码。
之后,ASCII 编码标准又于 1972 年被 ISO/IEC 采用,制定为ISO/IEC 646标准(ISO,即国际标准化组织 International Standardization Organization,成立于 1946 年;IEC,即国际电工技术委员会 International Electrotechnical Commission,成立于 1906 年;ISO/IEC 往往用来表示由这两大国际组织联合制定的标准)。因此,ISO/IEC 646(常简称为 ISO 646 )与 ASCII 指的是同一个编码标准。
ps:ASCII 码并不能完全等同 ISO 646,只能说大部分国家兼容 ASCII,某些国家甚至不兼容。ISO 646 标准。它是一个 7 比特字符的字集,来自数个国家标准。ISO 646 除了英语字母和数字部分所有国家相同,其他都可按照实际需要,把 ISO 646 做出适合该国文字的修改。又因为当年 8 比特字符集并未得到普遍接纳(特指 ISO 2022),各国把不同的字母或符号放进它们的字符集,以致部分出现在 ASCII 的字母或符号,并没有出现在某些国家的 ISO 646 变体之中 --- 维基百科。
由于 ASCII 码要晚于 EBCDIC 码出现(网上也有文章说是 ASCII 码要早于 EBCDIC 码开始设计,但 1968 年 ASCII 码才正式确定为标准),ASCII 码的编码方式参照了 EBCDIC 码,并吸取了其经验教训,将英文字母进行了连续排列,这方便了程序处理。
ps:ASCII 码是 1963 年就公布了(说明 1963 年之前就在研究这个),只是没标准化。EBCDIC 是 1963 年 - 1964 年之间研发出来。所以个人也认为 ASCII 更早。
ASCII 编码方案虽然不是最早出现的字符编码方案,但目前却是最基础、最重要、应用最广泛的字符编码方案。
目前所通行的其他字符编码方案,比如 ISO 8859 系列、GB系列(GB2312、GBK、GB18030、GB13000)、Big5、Unicode 等等,均直接或间接兼容 ASCII 码。
而像 EBCDIC 这样与 ASCII 完全不兼容的编码方案,基本上处于已淘汰或将要淘汰的境地。
ASCII 字符编码方案简介ASCII 码使用七个二进制数字(即比特)来表示一个字符,总共表示 128 个字符( 2^7 = 128,二进制编码为 0000 0000 ~ 0111 1111,对应的十进制就是 0~127 )。
由于目前计算机普遍采用 8 位作为一个字节来进行存取与处理,因此剩下最高位(即第 8 位)的那 1 比特一般为 0,但有时在一些通讯系统中也被用作奇偶校验位。
ps:1963 年刚公布 ASCII 的时候,由通信领域的协议采用了第 8 位(最高位)做校验纠错用途。但是,对于计算机内存来说,校验纠错变得不是必要的,用的不多(并不代表实际字符)。因此 8 位字符编码逐渐出现,用来表示比 ASCII 码更多的字符(也就是 ASCII 超集)。--- 来自维基百科 - ISO/IEC 2022 - 引言中的一段话。

ASCII 字符集共计有 128 个字符(见上表),码点编号(即字符编号,十进制表示是 Dec,十六进制表示是 Hex )从 0 到 127(二进制为从 0000 0000 到 0111 1111,十六进制为从 0x00 到 0x7F ),二进制最高位都是 0。其中:
码点编号 Dec 0 ~ 31:不可显示不可打印的控制字符或通讯专用字符,如 0x07( BEL 响铃)会让计算机发出哔的一声、0x00( NUL 空,注意不是空格)通常用于指示字符串的结束、0x0D( CR 回车)和 0x0A( LF 换行)用于指示打印机的打印针头退到行首(即回车)并移到下一行(即换行)等;
原作者注:将这些用于控制或通讯的控制字符或通讯专用字符称之为 “字符”,感觉上似乎有点怪,实际上这些所谓的 “字符” 表示的其实是一种动作或行为,因此才既不可显示也不可能打印。
码点编号 Dec 32:可显示但不可打印的空格字符;
码点编号 Dec 33 ~ 126:可显示可打印字符,其中 48 ~ 57 为 0 ~ 9 的阿拉伯数字,65 ~ 90 为 26 个大写英文字母,97 ~ 122 为 26 个小写英文字母,其余的是一些标点符号、运算符号等;
码点编号 Dec 127:不可显示不可打印的控制字符 DEL。
这时候的字符编解码非常简单,比如若要将字符序列编码为二进制流写入存储设备,只需要将该字符序列里的各个字符在ASCII 字符集中的字符编号(即码点编号),直接以一个二进制字节写入存储设备即可,字符编号就是字符编码,中间不需要经过特别的编码算法进行字符编号到字符编码的转换计算,更不存在所谓码元序列到字节序列的转换。
ps:这时候不要迷糊,原文所说的字符编号就是字符编码,意思是 ASCII 编码将不同的二进制 0 和 1 组合成码点编号,已经是最直接的二进制和字符对应映射关系了,不需要再转化。但是二进制可以用十进制(Dec)、十六进制(Dec)表示啊,毕竟人类对于十进制比较直观,二进制给机器看得嘛。
ASCII 的问题原文地址 - 阮一峰老师:在英语中,用 128 个符号编码便可以表示所有,但是用来表示其他语言,128 个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的 é 的十进制编码为 130(二进制 10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多 256个符号。
但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用 256 个符号的编码方式,代表的字母却不一样。比如,130 在法语编码中代表了 é,在希伯来语编码中却代表了字母 Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0 - 127 表示的符号是一样的,不一样的只是 128 - 255 的这一段。
至于亚洲国家的文字,使用的符号就更多了,汉字就多达 10 万左右。一个字节只能表示 256 种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是 GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536 个符号。
ASCII 到底是 7 位还是 8 位?前文说的比较清楚了,最开始老美使用时是 7 位共 128 个字符( ASCII ),可以称之为基础 ASCII 码。当时的第 8 位(最高位)并不是用来表示字符的一部分,而是纠错校验用的,只是用的不多,当时电脑也没什么必要。所以有许多 8 位字符编码(将这第 8 位实际应用上,来表示他们国家的字符,主要是西欧国家)逐渐出现,也就是 8 位共 256 个字符,可以称为ASCII 的超集、 8 位版、扩展 ASCII 等,他们将第 8 位字符应用到自己国家语言的字符表中,后 7 位与 ASCII 一致兼容,在使用 ASCII 编码范围时,第 8 位统一规定为 0,以便于计算机系统刚好以 1 个字节的方式来进行处理、存储和传输。
总结:有些地方称呼为 7 位 ASCII,实际使用都是带上了第 8 位,置 0,方便以一字节来处理、存储和传输。
附文 1:ASCII 艺术ASCII 艺术,又名“文字图”、“字符画”、“文字画”,这种主要依靠电脑表现的艺术形式是指使用电脑字符(主要是 ASCII )来表达图片。最早于1982 年美国卡内基梅隆大学出现,互联网刚出现时在英语世界的社交网( Usenet、BITNET、网络论坛、FidoNet、电子布告栏系统 BBS )上时常利用到的表情符号。它可以由文本编辑器生成。很多 ASCII 艺术要求使用定宽字体(固定宽度的字体,例如在传统打字机上使用的字体)来显示。
ASCII 艺术用于当文字比图像更稳定和更快显示的场合。包括打字机、电传打字机、没有图形的终端,早期的电脑网络,电子邮件和 Usenet 的新闻信息中。
ps:咱们主要出现在论坛签名,聊天、输入法中(搜狗称之为颜文字)

ASCII 艺术图形可以放到 HTML 文档中,但是通常要放置在 <pre> </pre> 格式文本标签中,以使得字体可以正确以等宽字体显示。另外,也可以通过 CSS 的方式在 HTML 中生成 ASCII 艺术。
此外还有 ASCII 立体图( ASCII stereogram ),是 ASCII 艺术的一种形式,它利用观察图像时,令视线产生适当的交叉从而生成三维图像的错视。有需要自行了解
附文 2:ASCII 丝带行动ASCII 丝带行动是一个网络爆红现象,于 1998 年发动,于 2013 年 6 月正式终止,提倡电子邮件须以纯文本发送。
ASCII 丝带行动的理据如下:
有一些电子邮件客户端不支持 HTML 电子邮件。这意味着接受电子邮件的人很可能无法阅读它,因为它将被显示作为未加工的 HTML 代码,甚至乎变成错误的信息。 其他客户有非常恶劣或残破的 HTML 解码,导致消息难以阅读。 HTML 电子邮件占很大的空间,且效率非常低。即使 HTML 电子邮件不包含任何图表,它的大小也大于纯文本邮件。大部分互联网用户的电邮账户有空间上限,超出时必须支付额外费用。 HTML 电子邮件的背景图像、华丽图表(例如 Incredimail 提供的),被行动支持者认为是浪费发件箱和收件箱的空间。为了几行文本而必须下载 200kb 或更大的电子邮件是相当可笑的。如果使用纯文本,在同样(甚至是百分之一)大小的邮件里可以准确地传达同样信息。ps:HTML 邮件,即各位邮箱经常收到的华丽广告(垃圾)邮件。(仅代表个人看法)
/o // The ASCII // Ribbon Campaign V/ Against HTML /A eMail! // () ascii ribbon campaign - against html e-mail / www.asciiribbon.org - against proprietary attachments _ ASCII ribbon campaign ( ) against HTML e-mail X / ~^~ // " / // // ------------------- @ // ///=SCII Ribbon Campaign X /=---=gainst HTML E- Mail X /// // ---------------------------- // //
EASCII 码
继续原文:计算机出现之后,首先逐渐从美国发展到了欧洲。由于欧洲很多国家所用到的字符中,除了基本的、美国也用的那 128 个 ASCII 字符之外,还有很多衍生的拉丁字母等字符。比如,在法语中,字母上方有注音符号;而欧洲其他国家也有各自特有的字符。
考虑到一个字节能够表示的编码实际上有 256 个(2^8 = 256),而 ASCII 字符却只用到了一个字节中的低 7 位(因此在ASCII 码中最高位总是为 0),编号为 0x00~0x7F(十进制为 0 ~ 127)。
也就是说,ASCII 只使用了一个字节所能表示的 256 个编码中的前 128 个(2^7 = 128)编码,而后 128 个编码相当于被闲置了。因此,欧洲各国纷纷打起了后面这 128 个编码的主意。
可问题在于,欧洲各国同时都有这样的想法。于是各国针对后面的 0x80~0xFF(十进制为 128~255)这 128 个编码分别对应什么样的字符,就有了各自不同的设计。
ps:这就是 ISO 646 的不同变种版本,甚至少数国家改变了与基础 ASCII 码兼容部分。所以 ASCII 并不等同 ISO 646。
为了结束欧洲各国这种各自为政的混乱局面,于是又先后设计了两套统一的,既兼容 ASCII 码,又支持欧洲各国所使用的那些衍生字符的单字节编码方案:一个是 EASCII(Extended ASCII)字符编码方案,另一个是 ISO/IEC 8859 字符编码方案。
ps:EASCII 于 1964 年就推出了 ISO 8859 系列,1982 年,ANSI 与 ECMA 合作开启此项工作。1985 年,公布了 ECMA - 94,即后来被采纳标准化的 ISO/IEC 8859 parts 1, 2, 3, 4。第 5、6、7、8、9、10、11、12、13、14、15、16 部分分别公布于 1988 年、1987 年、1987 年、1987 年、1989 年、1992 年、2001 年、1997 年(正式宣布放弃研发)、1998 年、1998 年、1999 年、2001 年。
先来说 EASCII 码。EASCII 码同样也是将 ASCII 中闲置的最高位(即首位)用来编码新的字符(这些 ASCII 字符之外的新字符,其最高位总是为 1 )。换言之,也就是将一个字节中的全部 8 个比特位用来表示一个字符。比如,法语中的 é 的编码为 130(二进制 1000 0010)。
显然,EASCII 码虽与 ASCII 码一样使用单字节编码,但却可以表示最多 256 个字符(2^8 = 256),比 ASCII 的 128 个字符(2^7=128)多了一倍。
因此,在 EASCII 码中,当第一个比特位(即字节的最高位)为 0 时,仍表示之前那些常用的 ASCII 字符(实际的二进制编码为 0000 0000 ~ 0111 1111,对应的十进制就是 0~127),而为 1 时就表示补充扩展的其他衍生字符(实际的二进制编码为 1000 0000 ~ 1111 1111,对应的十进制就是 128~255)。
这样就在 ASCII 码的基础上,既保证了对 ASCII 码的兼容性,又补充扩展了新的字符,于是就称之为 Extended ASCII(扩展 ASCII)码,简称 EASCII 码。
EASCII 码比 ASCII 码扩充出来的符号包括表格符号、计算符号、希腊字母和特殊的拉丁符号,如下表所示。

不过,EASCII 码目前已经很少使用,常用的是 ISO/IEC 8859 字符编码方案。
ISO 8859 系列
ISO 8859 系列方案与 EASCII 码类似,也同样是在 ASCII 码的基础上,利用了 ASCII 的7位编码所没有用到的最高位(首位),将编码范围从原先 ASCII 码的 0x00~0x7F(十进制为 0~127),扩展到了 0x80~0xFF(十进制为128~255)。
1982 年,ANSI 与 ECMA 合作开启此项工作,1985 年,公布了 ECMA - 94,1988 年,被采纳标准化为 [ISO/IEC 8859 parts 1](ISO/IEC 8859 parts 1), 2, 3, 4。
需要注意的是,ISO 8859 字符编码方案所扩展的这 128 个编码中,实际上只有 0xA0~0xFF(十进制为 160~255)被实际使用。也就是说,只有这 96 个编码定义了字符,而 0x80~0x9F(十进制为128~159)这 32 个编码并未定义字符。
显然,ISO/IEC 8859 (即 ISO 8859 )字符编码方案同样是单字节编码方案,也同样完全兼容 ASCII。
更需要注意,与 ASCII、EASCII 属于单个独立的字符集不同,ISO/IEC 8859 是一组字符集的总称,其下共包含了 15 个字符集,即 ISO/IEC 8859-n,n=1、2、3...15、16(其中12未定义,所以共15个)。ps:详见维基百科 --- 点击上方链接即可跳转。
这 15 个字符集大致上包括了欧洲各国所使用到的字符(甚至还包括一些外来语字符),而且每一个字符集的补充扩展部分(即除了兼容 ASCII 字符之外的部分)都只实际使用了 0xA0~0xFF(十进制为160~255)这 96 个编码。
ps:也就是说除了基础 ASCII 部分相同,其他扩展的 96 个编码是各自不兼容的。
ISO-8859-X 字符集是特定的把 ISO-2022 的若干成分组合起来的字符集。这些成分包括:
低端控制字符(C0) ASCII 字符集(GL) 高端控制字符(C1) 高端字符(GR)是特定于每个 ISO-8859-X 变种。例如 ISO-8859-1 是由 ISO-IR-1,ISO-IR-6,ISO-IR-77,ISO-IR-100 组成。C0、GL、C1、GR 等等概念,在 ISO 2022 和 ISO 8859 的维基百科中有详细介绍,想深入了解的可点击链接查看。
其中,ISO/IEC 8859-1 收录了西欧常用字符(包括德法两国的字母),目前使用得最为普遍。ISO/IEC 8859-1 往往简称为 ISO 8859-1,而且还有一个称之为 Latin-1(也写作 Latin1 )的别名。

注意:图片标题前面的 “Codepage 819” 表示 ISO 8859-1 编码的代码页编号为 819,有关 “代码页” 的介绍后文有详细介绍。
其余从 ISO 8859-2 到 ISO 8859-16 各自所收录的字符如下:
ISO 8859-2 字符集,也称为 Latin-2,收录了东欧字符; ISO 8859-3 字符集,也称为 Latin-3,收录了南欧字符; ISO 8859-4 字符集,也称为 Latin-4,收录了北欧字符; ISO 8859-5 字符集,也称为 Cyrillic,收录了斯拉夫语系字符; ISO 8859-6 字符集,也称为 Arabic,收录了阿拉伯语系字符; ISO 8859-7 字符集,也称为 Greek,收录了希腊字符; ISO 8859-8 字符集,也称为 Hebrew,收录了西伯莱(犹太人)字符; ISO 8859-9 字符集,也称为 Latin-5或Turkish,收录了土耳其字符; ISO 8859-10 字符集,也称为 Latin-6 或 Nordic,收录了北欧(主要指斯堪地那维亚半岛)的字符; ISO 8859-11 字符集,也称为 Thai,几乎与泰国国家标准 TIS-620(1990)字符集等同, 唯一的区别是,ISO 8859-11 定义了不间断空格 NBSP(non-breaking space)字符(码点值为 0xA0),而 TIS-620 中则未定义该字符; ISO 8859-12 字符集,目前尚未定义(未定义的原因目前有两种说法:一是原本要设计成一个包含塞尔特语族字符集的 “Latin-7”,但后来塞尔特语族变成了 ISO 8859-14 / Latin-8;二是原本预留给印度天城体梵文的,但后来却搁置了); ISO 8859-13 字符集,也称为 Latin-7,主要函盖波罗的海(Baltic)诸国的文字符号,也补充了一些被 Latin-6 遗漏的拉脱维亚(Latvian)字符; ISO 8859-14 字符集,也称为 Latin-8,它将 Latin-1 中的某些符号换成塞尔特语(Celtic)的字符; ISO 8859-15 字符集,也称为 Latin-9,或者被戏称为 Latin-0,它将 Latin-1 中较少用到的符号删除,换成当初遗漏的法文和芬兰字母,还把英镑和日元之间的金钱符号,换成了欧盟货币符号; ISO 8859-16 字符集,也称为 Latin-10,涵盖了阿尔巴尼亚语、克罗地亚语、匈牙利语、意大利语、波兰语、罗马尼亚语及斯洛文尼亚语等东南欧国家语言。ISO 2022 系列
在前文介绍的 EASCII 码和 ISO 8859 码发布时间线之间(1964 年 - 1988 年),还有一个重要的字符编码 ISO 2022。
因为 8 位字符编码逐渐出现,用来表示比 ASCII 码更多的字符。为此,1971 年公布的 ECMA-35 标准,用来规定各种 7 位或 8 位字符编码应当遵从的共同规则。随后 ECMA-35 被采纳为 ISO 2022。其特别以东亚语言:汉语文字、日语文字或朝鲜文字的编码方法著称。
英语可用 7 位编码储存,而其他使用拉丁字母、希腊字母、西里尔字母、希伯来字母等的语文,由于只使用数十个字母,传统上均使用 8 位编码的 ISO/IEC 8859 标准来表示。但由于汉语、日语及朝鲜语字数众多,无法用单一个 8 位字符来表达,故需要多于一个字节来代表一个字。于是,ISO 2022 就设计出来让汉语、日语及朝鲜语可以使用数个 7 位编码的字符来示。
ISO 2022 用来:
在一种字符编码下表示属于多个字符集的字符; 表示大字符集; 兼容 7 比特信道,即使是 8 比特编码字符集。ISO 2022 使用 “转义符串”(Escape sequence)指出随后的字符属于哪个字符集。这些字符集在 ISO 登记,并遵循 ISO 2022 标准规定的模式。转义符串由 1 个 “ESC” 字符(0x1B),再由两至三个字符串组成。此标记代表它后面的字符,属于下表字符集的文字。对于一个字符集,如果上下文可以判明是哪种字符集,也可以不通过转义序列来明确指出是哪种字符集。实际上,ISO-8859-1 就宣布不需要定义它的转义序列。详细原因可点击 ISO-8859-1查看。
ISO 2022 等同于欧洲标准组织(ECMA)的 ECMA-35。中国国标 GB 2312、日本工业规格 JIS X 0202(旧称 JIS C 6228 )及韩国工业规格 KS X 1004(旧称 KS C 5620 )均遵从 ISO 2022 规范。
下一篇 字符编码(二:简体汉字编码与 ANSI 编码 )
相关知识
模糊查询和转义字符.doc
简单的查询语法
使用中文字符作为sql的传参,导致查询不出结果
编码规范参考
6 字符型数据及其处理
Linux内字符串无法显示原因解析 (linux字符串不显示) – 后浪云
c++ TCHAR转string导致中文缺失或乱码问题及解决
字符串查找、错误信息、字符分类函数
植物科学数据分类与编码——植物科学数据中心
解决Error:invalid character in identifier
网址: 字符编码(一:术语及字符编码由来)面对字符编码,经过各种资料与维基查询,想自己写一篇笔记总结一下,写到一半发现了一位大佬 https://www.huajiangbk.com/newsview105130.html
上一篇: 赫特福德大学本科生专业有什么 |
下一篇: 最新毕业设计项目(通用7篇) |
推荐分享

- 1君子兰什么品种最名贵 十大名 4012
- 2世界上最名贵的10种兰花图片 3364
- 3花圈挽联怎么写? 3286
- 4迷信说家里不能放假花 家里摆 1878
- 5香山红叶什么时候红 1493
- 6花的意思,花的解释,花的拼音 1210
- 7教师节送什么花最合适 1167
- 8勿忘我花图片 1103
- 9橄榄枝的象征意义 1093
- 10洛阳的市花 1039