字符编码一(编码基础知识)

发布时间:2024年01月04日

前言

计算机一开始发明出来时是用来解决数字计算问题的,后来人们发现,计算机还可以做更多的事,例如文本处理。为了在计算机上也能表示、存储和处理像文字、符号等等之类的字符,就必须将这些字符转换成二进制数字。当然,肯定不是我们想怎么转换就怎么转换,否则就会造成同一段二进制数字在不同计算机上显示出来的字符不一样的情况,因此必须得定一个统一的标准进行转换。于是就设计出了进行这种转换的标准——字符编码标准。
字符编码的复杂性所引发的困惑,主要不在于其技术深度,而主要在于因其历史演变与沿革所导致的概念混乱和认知偏误。也就是说,很多人一开始,就被各种以讹传讹的说法(包括网络上的各种貌似专业、权威的百科词典和技术文章)给误导了、给带偏了,导致看的资料越多,困惑反而越多。本文章的目的就是为了重新进行细致梳理,厘清各种概念的边界,消除模糊地带,以正本清源、一扫迷雾。
另外首先要明确的是,字符编码针对的对象是文本(字符),因为计算机只能处理数字,如果要处理文本(如字符和字符串)就必须先经文本转换为数字才能处理,这就涉及到了字符编码问题

这里我准备两篇博客来归纳总结,第二篇博客是编码发展的过程介绍,用来辅助理解GB相关编码方式,参考了解下即可。
博客部分内容参考了知乎上相关内容,可参见字符编码参考

1.字符编码中涉及的常见术语:

字符编码涉及到很多基本的概念,这些概念是否能清楚的区分对于理解字符编码的架构和关系至关重要。有些概念在其他文章中出现了滥用和误用,有些概念是随着技术的发展原本的同义词出现了分化,有些概念要结合语境区分。这里面要注意区分的是4)-9)容易混淆的概念。
1) 编码:是信息从一种形式转换为另一种形式的过程,比如用预先规定的方法将字符(文字、数字、符号等)、图像、声音或者其他对象转换成规定的电脉冲信号或者二进制数字。
2) 解码:编码的逆过程。
3) 字符:则指的是各种文字和符号的总称,包括文字、数字、字母、音节、标点符号、图形符号等,图像符号比如下图所示我们现在微信等通信软件常用的Emoji表情符就是不断扩展到Unicode字符表中的。
在这里插入图片描述4)字符表(Character Repertoire),就是该编码方案支持的所有字符,从软件角度可以字节为一维数组,数组中包含了所有支持的字符。如ASCII编码支持128个字符,128个字符就是字符表。
5) 字符集(Character Set、Charset),我们这里是在字符表的基础上包括了字符编号。但是我们会笼统的 说常见的字符集有ASCII字符集、ISO 8859系列字符集(ISO 8859-1~8859-16)、GB系列字符集(GB2312、GBK、GB18030)、BIG5字符集、Unicode字符集等,字符集不仅定义了字符的集合,还定义了每个字符对应的编号,字符集,等于包含了字符和编号概念。
6) 字符编号:字符编号和字符编码的概念就是随着技术的发展逐渐区分开的。早期在ASCII编码方案阶段,这两个概念是没有区别的,因为字符使用的编码就是字符的编号;在ANSI编码方案阶段,微软将不同地区不兼容的编码方式使用代码页进行划分时,代码页中的编号也同样就是字符的编码(不过GB自身定义时实际上类似有区位码和机内码概念等效于编号和编码的关系,而ANSI代码页时将字符和编码直接映射,但是作为软件开发者并不需要再了解GB);而在Unicode编码方案阶段,字符编号和字符编码就更明显的进行了分层划分。
7)字符编码:即在字符集与指定集合两者之间建立一个对应关系(即映射关系)的过程。我们认为编码就是最终表现在输入计算机的数值。
8)字符编码方式:也就是字符编码的实现方法,同字符编号和字符编码类似,字符编码方式和字符编码方案在技术发展的早期是一个概念(因为早期技术比较简单,体现不出方式和方案的差别)。比如早期ASCII编码方式,你也可以说也是ASCII编码方案,因为整个编码方式就是一个字符集和编码(也是编号)。ISO 8859、GB2312、GBK、GB18030、BIG5、UTF-8、UTF-16、UTF-32也都表示的是编码方式,即将字符转换为字符编码的映射方法。
9)字符编码方案:我们说字符编码方案、编码标准、编码系统认为是同义词,说的是一套完整的编码系统。完成的编码方案从历史顺序分为ASCII编码方案、ANSI编码方案、Unicode编码方案。
10)字符编码模型:是将编码方案抽象进行分层的概念,直到Unicode编码方案阶段,才有了5层结构。模型分为传统字符编码模型和现代字符编码模型,ASCII编码方案、ANSI编码方案称为传统编码模型,Unicode编码称为现代编码模型。实际上倒也不需要这么较真,ASCII编码方案、ANSI编码方案也可以按照5层结构进行划分,只不过有些层相当于是固定或默认的而已。
11)ANSI:单纯说ANSI的话,ANSI是美国的一个非营利组织——美国国家标准学会(American National Standards Institute)的缩写,ANSI这个组织做了很多标准的制定工作,包括前面介绍过的字符编码标准ASCII、C语言标准ANSI C,还有与各国和地区既兼容ASCII又互相不兼容的字符编码方案相对应的“代码页(Code Page)”标准。而说ANSI编码,则指的是被微软统称为既兼容ASCII又互相不兼容的非Unicode字符编码。比如ANSI规定简体中文GB编码的代码页是936,所以GB编码又叫做ANSI Code Page 936(ANSI标准的代码页936)。
12)代码页:上面讲了ANSI制定了代码页标准,简而言之,代码页就是字符和编码(也是编号)的映射关系表。不同地区的编码方式ANSI取了不同的页码,比如ANSI规定简体中文GB编码的代码页是936,Shift JIS日文字符集的OEM代码页932。第3章说了代码页和GBK的关系,不细分可认为两者是相同概念即可。(在Windows中,代码页是系统默认设置的,也可在“控制面板-区域和语言-管理-非Unicode程序的语言-更改系统区域设置”中选择列表中的语言进行更改)

2.历史变革

按照编码方案的历史演变,可以 简单分为三个阶段
图1第一阶段:ASCII编码方案阶段,ASCII编码方案主要适用于美国(与美国同文的英语国家大致上也适用),这也就是我们平时使用的Ascii码,只需要一个字节解决128字符编码问题。
第二阶段:ANSI编码方案阶段,随着计算机发展到欧洲各国以及世界各地,开始发展到第二阶段,各个国家和地区先后各自独立的指定了兼容ASCII编码但相互之间又不兼容的ANSI编码方案。需要注意的是,ANSI编码方案并不单指某一特定编码方案,明确的说,ANSI编码是对世界各个国家和地区所制定的既兼容ASCII码又互相之间不兼容的各种字符编码方案(这里叫编码方式更准确,不过早期编码方式和编码方案等价)的统称,微软统称为ANSI编码。所以只知道是ANSI编码,还需要进一步知道是ANSI编码方案下的具体哪种编码方式才能正确编解码。在微软Windows系统的编码处理中,ANSI编码一般代表系统默认非Unicode程序的编码方式,比如在简体中文操作系统中ANSI编码默认指的是GB系列编码(GB2312、GBK、GB18030);在繁体中文操作系统中ANSI编码默认指的是Big5编码(港澳台地区使用的繁体汉字编码);(Windows10简体中文非Unicode对应的是GBK编码,后面介绍电脑在哪里配置ANSI具体的编码方式)。
注:ANSI实际上扩展了UTF-xxx编码方式的代码页,但是实际上只有非Unicode程序采用ANSI,UTF-xxx编码方式的代码页并没什么使用。
第三阶段:Unicode编码方案阶段,由于ANSI编码方案阶段各自为政互不兼容,给世界各个国家和地区基于计算机的跨区域交流带来的极大的不便,之后在统一码联盟与ISO/IEC(即国际标准化组织/国际电工委员会)的共同努力之下,终于通过Unicode/UCS编码方案的制定,发展到了现在的全球统一编码的第三阶段,最终Unicode编码方案胜出而一统编码江湖。(Unicode把所有表示的字符整理一个统一的字符集,并且字符集可扩展)
下表为技术发展过程下编码方案和编码方式关系:
在这里插入图片描述忽略代码页和具体编码方式的差异,编码方案和编码方式的关系如下:

在这里插入图片描述

在早期的ASCII和ANSI代码页传统编码模型,字符集和字符编码并没有区别,字符集一旦确定,字符的编号就是该字符的编码。在现代Unicode字符编码模型中,字符集和字符编码方式没有绑定在一起,字符集只是将所有表示的字符设计成一个集合,并没有(字符编码方式和字符集脱钩了),比如UTF-8、UTF-16、UTF-32都是用的Unicode字符集,他们是不同的编码方式。

3.ANSI编码代码页936和GB的关系:

前面说到,Windows系统中的CP936代码页实际上是GBK编码方案的一个实现。实际上从GB系列编码(GB2312、GBK、GB18030)自身的设计,其设计了字符集,字符集有对应的编号,这个编号呢并不是像ASCII一样就是最终的编码,而是称它为区位码,区位码需要加一个固定值转换为机内码(或内码),这个机内码才是最终的编码(针对GBK而言他的编号并不是编码)。而代码页936作为一个字符集存储的是一个字符和最终编码的映射关系,也就没有区位码的概念,所以代码页也称为内码表。当然,我们不需要了解这么多,知道ANSI代码页936用的GBK编码方式就行了,什么区位码还是机内码不需要了解,反而代码页和ASCII表一样直接存储的就是字符和编码的关系,编号也是编码。

4.字符编码模型

字符编码模型可以分为传统字符编码模型和现代字符编码模型,区别在于是否字符集和字符编码方式是绑定的。传统字符模型是以ASCII编码方案和ANSI编码方案为代表,
现代字符编码模型以统一码(Unicode)和通用字符集(UCS)为代表,没有直接采用ASCII这样的简单字符集的编码思路,而是采用了一个全新的编码思路。
这个全新的编码思路将字符集与字符编码的概念更为细致地分解为了以下几个方面:

  1. 有哪些字符
  2. 这些字符的编号是什么
  3. 这些编号如何编码成一系列逻辑层面有限大小的数字,即码元序列
  4. 这些逻辑层面的码元序列如何转换为(即映射为)物理层面的字节序列(即字节流)
  5. 在某些特性传输协议的传输环境中(比如Email中),再进一步将字节序列进行适应性编码处理
    上述方便作为一个整体,否成了现代字符编码模型。
    同一个字符集,可以通用于不同的编码方式;也就是说,可以采用不同的编码方式来对同一个字符集进行编码。字符集与编码方式之间的关系可以是一对多的关系。更进一步而言,在传统字符编码模型中,字符编码方式与字符集是紧密结合在一起的,字符编码方式确定,对应的字符集就是确定的;而在现代字符编码模型中,字符编码方式与字符集脱钩了,字符集是公用的一份

传统ASCII:字符集唯一->字符编号即字符编码
传统ANSI编码:先选择编码方式->对应确定的字符集->字符编号转换为字符编码
现代Unicode编码:字符集统一规定(CCS)->选择编码方式(CEF)->选择字符编码模式(CES),对应唯一的物理字节序。

4.1 现代编码模型

专指Unicode编码模型,分为5层,第5层是涉及一些专用上层协议做的适应性转换,如不专用该领域可不考虑。
第一层:抽象字符表ACR (Abstract Character Repertoire抽象字符清单):明确字符的范围(即确定支持哪些字符),抽象字符表ACR,按英文字面意思直译为抽象字符清单,是一个编码系统支持的所有抽象字符的集合。Unicode字符表是开放式的,即字符范围是不固定的,允许不断添加新的字符,比如我们微信或其他软件使用的很多Emoji表情符就被源源不断地加入进来。
第二层:编号字符集CCS(Coded Character Set):将字符表映射到一个非负整数或非负整数值对的集合,换句话说就是字符和对应字符编号的集合。
注:(在传统编码模式中,没有单独将字符表列出(因为太简单了呀),如ASCII表就是字符集,是字符表和编号的映射关系,而编号就是编码,)
第三层:字符编码方式CEF(Character Encoding Form):编号字符集里字符编号(即码点编号、码点值),转换成或者说编码成有限比特长度的编码值(即字符编码)的方式,指定编码方式后得到了是逻辑编码,也就是未按编码数据类型进行字节序转换前的编码。
在ASCII这样传统的、简单的字符编码系统中,并没有也不需要区分字符编号与字符编码,可认为字符编号就是字符编码,字符编号与字符编码之间是一个直接映射的关系。而在Unicode这样现代的、复杂的字符编码系统中,则必须区分字符编号与字符编码,字符编号不一定等于字符编码,字符编号与字符编码之间不一定是一个直接映射的关系,比如UTF-8、UTF-16为间接映射,而UTF-32则为直接映射。
第四层: 字符编码模式CES(Character Encoding Scheme):将字符编码(码元序列)转换为字节序列。其实就是多个字节的字符编码涉及处理、存储和传输在计算机的大小端字节序问题。传统编码模型由于编码基本数据类型是字节,故不涉及该问题,Unicode方案单独针对该层和第5层进行了划分。
第五层 传输编码语法TES(Ttransfer Encoding Syntax):将字节序列作进一步的适应性编码处理,该部分是针对特定协议扩展的,平时我们可能接触不到,可以不考虑。
1)一种是把字节序列映射到一套更受限制的值域内,以满足某种特殊传输环境的限制,例如用于Email传输的Base64编码或者quoted-printable编码(即可打印字符引用编码),都是把8位的字节映射为7位长的数据。注:Email协议设计为仅能传输7位的ASCII字符;从一个8位字节的角度来看,ASCII字符的首位始终为0,所以除去首位的0,实际有效的位数为7位,或许最初是出于节约传输流量的考虑,Email协议被设计为了仅能传输7位的ASCII字符。
2)另一种是压缩字节序列的值,如LZW或者进程长度编码等无损压缩技术。
总结:传统编码模型按照分层包括了第一层到第三层(因为传统编码模型以字节为编码基本数据类型,故不存在第四层的问题)。

4.2 字符编码模式补充说明

Unicode现代编码模型中为什么需专门定义这一层来说明字符编码转换为字节序列?我们知道大小端序和处理器和操作系统都有关系,传统的ANSI也存在多字节编码为什么不存在大小端的问题,这是因为传统ANSI多字节编码也是以字节为单位的(编码模型将编码最小单元叫做码元,其实就是编码基本数据类型),所以ANSI也不存在字节序问题。而Unicode下的UTF-XXX编码方式,其编码数据类型是以XXX为宽度的,比如UTF-8以单字节为单位作为编码数据类型,UTF-16以双字节为编码基本数据类型,UTF-32以四字节为编码基本数据类型,所以UTF-16和UTF-32必然涉及到了大小端转序问题。
(UTF-8为变长类型编码,即一个字符可能对应1-多字节编码,UTF-16编码方式下一个字符一般是一个或两个双字节编码,UTF-32的字符只需要一个四字节进行编码)

5.UTF-8编码方式

这里单独将Unicode编码方案中的UTF-8进行下说明,因为UTF是目前应用最广泛的一种Unicode编码方式(但不是最早面世的,UTF-16要早于UTF-8面世,UTF-32基本淘汰)。
UTF-16对于ASCII字符也必须使用两个字节(因为16位为其基本编码数据类型)进行编码,存储和处理效率相对低下,并且由于ASCII字符经过UTF-16编码后得到的两个字节,高字节始终是0x00,很多C语言的函数都将此字节视为字符串末尾从而导致无法正确解析文本。因此,UTF-16一开始推出的时候就遭到很多西方国家的抵制,大大影响了Unicode的推行。于是后来又设计了UTF-8编码方式,才解决了这些问题。
UTF-8以单字节为基本数据类型进行编码,因为基本数据类型较小的缘故,UTF-8使用一个至多个单字节的序列来表示Unicode字符,UTF-8是一种使用单字节码元的变宽(即变长或不定长)码元序列的编码方式。

6.文件类型带不带BOM,这个BOM是什么

像Unicode编码方式UTF-16或UTF-32这种编码基本数据类型是多字节的编码方式,将逻辑编码转换到字节编码前,由于系统平台的差异,存在字节序的问题。导致在某些场合下,需要对文本字符所采用的字节序予以明确说明。Unicode/UCS规范中所推荐的用于说明字节序的方法是使用BOM字节序标记(Byte-Order Mark字节顺序标记)。
(注意:所有的ANSI编码都是单字节作为编码基本数据类型,不涉及字节序的问题,因此也不需要BOM).
现在的文件都是在文件头放BOM标记来表明自身的字节序。下面是BOM标记在不同的编码方式下的格式:
在这里插入图片描述

6.1 UTF-8为什么有带BOM的文件

Windows系统中BOM有时也用在UTF-8编码的文本文件的开头,虽然UTF-8编码并不存在字节序问题,但Windows却用BOM来表明该文本文件的编码格式为UTF-8,这看起来有点“多此一举”,实际在UTF-8文件时,带BOM只是为了告诉解析文件的程序我是UTF-8类型的编码文件(而不带BOM的文件其实都是没有文件类型的)。

6.2 文本编辑软件工作原理

文本编辑软件比如记事本或者notepad++、UltraEdit等,准确来讲,其是编辑器(编码)和解码器。
一般文本编辑软件打开文本确定文本编码方式的方法有如下三种:

1.检测文件头标识(这种针对带BOM的文件)
2.显式的提示用户手动选择(linux系统下提示打开的文件格式)或者我们软件在编程过程中软件调用接口打开文件时指定是什么编码格式打开。
3.根据软件设定的规则自行推断。这种比如在文件不带BOM头,记事本会按照你的编码进行解析,每个软件的编码解析优先级可能有区别,软件可以正常解析,就按照自己的算法推算出你是该类型的编码格式,软件执行解码。
(注意,并不是你将一个文本保存成某个格式里面的内容再打开后就更新成某种格式了,这种就跟你讲txt文件后缀名改为xlsx并不代表你内容就是excel文件格式,打开照样乱码),换句话说,编辑软件其实不会知道你保存的文件类型后缀是UTF-8不带BOM就认为你是UTF-8类型了。

6.3 windows记事本常用编码方式

在这里插入图片描述
windows记事本的常见编码方式命名解释如下:
1)ANSI,指的是对应当前系统区域设置(即系统locale)中的默认ANSI编码,不带BOM。在简体中文版Windows系统中,默认的ANSI编码,指的就是GBK编码,即CP936代码页。(本身GB编码方式就不涉及字节序的问题)
2)UTF-16LE,是指带有BOM的小端序UTF-16。
3) UTF-16BE,是指带有BOM的大端序UTF-16。
4) UTF-8,是指不带有BOM的UTF-8。
5) 带有BOM的UTF-8

6.4 记事本、notepad++、UltraEdit使用说明

windows的记事本随着版本更新,其默认创建的txt文件的编码方式也有变化,以下图笔记本为例,这是windows10的20H2版本,可以看到默认创建的文件类型是UTF-8不带BOM的编码方式(而早期的笔记本版本有的默认新建的文件是ANSI编码方案简体中文下即GBK编码方式)。
在这里插入图片描述

文章来源:https://blog.csdn.net/u011116085/article/details/135250704
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。