美文网首页
字符的编码(一)

字符的编码(一)

作者: 古剑诛仙 | 来源:发表于2019-06-27 12:27 被阅读0次

    内容摘自https://zhuanlan.zhihu.com/p/27012715

    前言

    一、

    字符编码是计算机世界里最基础、最重要的一个主题之一。不过,在计算机教材中却往往浮光掠影般地草草带过,甚至连一本专门进行深入介绍的著作都找不到(对这一点我一直很困惑,为什么就没有哪位大牛对这个如此基础、重要而又如此容易让人困惑的主题写一本专著予以介绍呢)。

    而在编程实践中,如果不发扬死磕到底的精神将字符编码问题的来龙去脉、前世今生彻底搞清楚,那么它终将会像幽灵一样挥之不去,导致时不时地被各种与字符编码相关的“灵异”事件折磨得死去活来。

    本人正是在经受了字符编码所带来的种种令人崩溃的痛苦之后,才在痛定思痛之余,最终痛下决心,誓要将它刨根究底。

    二、

    字符编码的基础性、重要性,主要体现在它涉及面广。向下涉及到计算机的底层技术,甚至是硬件实现;向上几乎跟所有的操作系统、编程语言、应用程序都密切相关。

    因此,要想真正搞明白字符编码问题,必须得从计算机的基本概念——位、字节、字等等开始,再结合不同的系统环境与编程环境,进行具体分析。

    三、

    类似于字符编码这样基础、重要、应用广泛而又特别容易让人困惑的主题还有字节序(即大小端表示)、正则表达式以及浮点数实现、日期时间处理等等。其中,字节序、正则表达式跟字符编码的关系又密切相关,尤其是字节序,直接影响字符编码的字节序列。而由于正则表达式主要用于在字符串中查找、提取字符或子字符串,要想真正理解正则表达式,也离不开对字符编码的深入理解。

    基于此,本人准备将自己对字符编码(包括字节序)与正则表达式进行刨根究底后的一些心得体会写成两个系列文章,一方面整理一下自己的思路以备忘,另一方面也真心希望能够起到抛砖引玉的作用。
    常见的编码如下:


    image.png

    四、

    下面是字符编码系列文章将会涉及到的内容:

    一)关键术语解释:位、字节、字与字长、字符集、编码、解码、字符编码、现代字符编码模型

    二)字符编码的由来

    三)ASCII字符编码方案

    四)扩展ASCII字符编码方案EASCII以及ISO/IEC 8859系列字符编码方案

    五)汉字编码方案:GB2312、GBK、GB18030、GB13000、全角与半角、CJK中日韩统一表意文字

    六)汉字编码中区位码、国标码(交换码)、内码(机内码)、外码(输入码)、字形码(输出码)的区别及关系

    七)ANSI编码

    八)代码页(Code Page)、微软与ANSI代码页

    九)Unicode编码方案的面世

    十)Unicode编码方案概述

    十一)字符编码模型的演变与字节序

    十二)Unicode字符集的编码方式:码点、码元、UTF-8、UTF-16、UTF-32

    十三)同样是多字节编码,为什么说UTF-8没有字节序的问题,而UTF-16、UTF-32却有?

    十四)微软为什么跟联通有仇——Windows记事本的字符编码方式

    十五)Windows记事本的四种编码方式(ANSI、Unicode、Unicode big endian、UTF-8)有何区别?

    十六)深入剖析奇葩的Python字符编码

    十七)Vim中的字符编码问题

    十八)Unicode常见问题解答

    十九)总结

    (一)关键术语解释(上)

    一、位

    位,即比特(Bit),亦称二进制位、比特位、位元,指二进制数中的一位,是计算机中信息表示的最小单位。

    Bit是Binary digit(二进制数位)的缩写,由数学家John Wilder Tukey提出,习惯上以小写字母b表示,如8比特可表示为8b。

    每个比特位有0和1两个可能的值,除了代表数值本身之外,还可代表:

    数值的正、负;
    两种状态,如电灯的开、关,某根导线上电压的有、无,等等;
    抽象逻辑上的是、否,或者说真、假。

    二、字节

    在计算机中,通常都会使用一连串的位(比特),称之为位串(bit string比特串)。很显然,计算机系统都不会让你使用任意长度的位串,而是使用某个特定长度的位串。

    一些常见的位串长度形式具有约定好的名称,如,半字节(nibble,貌似用的不多)代表四个位的组合,字节(byte)代表8个位的组合;还有字(word)、双字(Double word,简写为Dword)、四字(Quad word,简写为Qword)、十字节(Ten byte,简写为Tbyte)。

    字节(byte),又称为位元组,音译为“拜特”(但很少使用这个译名),是计算机中计量存储容量和传输容量的一种基本计量单位,是由连续的、固定数量的位(即比特)所组成的位串(即比特串),一般由8个位组成,即1 byte = 8 bit。习惯上用大写的B表示,如3字节可表示为3B。


    image.png

    现代个人计算机(PC)的存储器编址,一般是以字节为单位的,称之为按字节编址,因此字节一般也是存储器的最小存取单元以及处理器的最小寻址单位(也有按位寻址、按字寻址等等,但在个人计算机上应用不普遍,这里不讨论)。

    字节作为存储器的最小存取单元以及处理器的最小寻址单位这一重要特点,跟字符编码的关系极为密切(比如,码元的单字节与多字节、字节序的大端序与小端序等,都与以字节为基础的基本数据类型密切相关,详见后文介绍)。

    习惯上,按照下面的图来排列一个字节上的各个位的顺序,即按照从右到左的顺序,依次为最低位(第0位)到最高位(第7位):


    image.png

    注意,字节不一定非得是8位,以前也有过4位、6位或7位作为一个字节的标准,比如IBM 701(36位字长,18位为一字节)、IBM 702(7位字长,7位为一字节)、CDC 6600(60位字长,12位为一字节byte)等,只是现代计算机的事实标准就是用8位来代表一个字节(最终形成这一事实标准除了历史原因和商业原因之外,最重要的原因应该是由于二进制的特性:2的次方计算更方便快捷)。

    正是因为这个原因,在很多较为严谨的技术规格文献中,为了避免产生歧义,更倾向于使用8位组(Octet)而不是字节(Byte)这个术语来强调8比特位串。

    不过,由于大众基本上都将字节理解为8比特位的8位组,因此一般文章中如果未作特别说明,基本上都将8位组直接称之为字节。

    三、字与字长

    虽然字节是大多数现代计算机的最小存储单元和传输单元,但并不代表它是计算机可以最高效地处理的数据单位。

    一般来说,计算机可以最高效地处理的数据大小,应该与其字的字长相同,这就涉及到了字及字长的概念。

    字:在计算机中,一串比特位(位串、比特串)是作为一个整体来处理或运算的,这串比特位称为一个计算机字,简称字。字通常分为若干个字节(每个字节一般是8位)。
    字长:即字的长度,是指计算机的每个字所包含的位数。字长决定了CPU一次操作所处理的实际比特位数量的多少。字长由CPU对外数据通路的数据总线宽度决定。

    计算机处理数据的速率,显然和它一次能加工的位数以及进行运算的快慢有关。如果一台计算机的字长是另一台计算机的两倍,若两台计算机的速度相同,在相同的时间内,前者能做的工作一般是后者的两倍。因此,字长与计算机的功能和用途有很大的关系,是计算机的一个重要技术指标。

    在目前来讲,桌面平台的处理器字长正处于从32位向64位过渡的时期,嵌入式设备基本稳定在32位,而在某些专业领域(如高端显卡),处理器字长早已经达到了64位乃至更多的128位

    四、编码与解码

    编码(Encode),是信息从一种形式或格式转换为另一种形式或格式的过程,比如用预先规定的方法将字符(文字、数字、符号等)、图像、声音或其它对象转换成规定的电脉冲信号或二进制数字。
    解码(Decode),为编码的逆过程。

    五、字符集

    字符集(Character Set、Charset),字面上的理解就是字符的集合,是一个自然语言文字系统支持的所有字符的集合。字符是各种文字和符号的总称,包括文字、数字、字母、音节、标点符号、图形符号等。

    例如ASCII字符集,定义了128个字符;GB2312定义了7445个字符。而计算机系统中提到的字符集准确地来说,指的是已编号的字符的有序集合(但不一定是连续的,后文有详细介绍)。

    常见字符集有ASCII字符集、ISO 8859系列字符集、GB系列字符集(GB2312、GBK、GB18030)、BIG5字符集、Unicode字符集等。

    image.png
    六、字符编码

    字符编码(Character Encoding),是把字符集中的字符按一定格式(或者说形式、方式)编码为某指定集合中某一对象(比如由0和1两个数字所组成的位串模式、由0~9十个数字所组成的自然数序列、电脉冲等)的过程,亦即在字符集与指定集合两者之间建立一个对应关系(即映射关系)的过程。这是信息处理的一项基础技术。

    而在计算机科学中,通常以字符集来表达信息,以计算机为基础的信息处理系统则利用电子元件(硬件)的不同状态的组合来表示、存储和处理信息。

    电子元件不同状态(一般是开和关或称为开和闭两种状态)的组合能代表数字系统中的数字(比如开和关代表二进制中的0和1),因此字符编码的过程也就可以理解为将字符转换映射为计算机可以接受的二进制数字的过程,其目的是为了便于字符在计算机中表示、存储、处理和传输(包括在网络中传输)。

    常见的例子包括将拉丁字母表编码成摩斯电码和ASCII码。其中,ASCII将字母、数字和其它符号进行编号,并且在计算机中直接用7比特的二进制数字来表示这个编号。通常会额外地在最高位(即首位)再增加一个扩充的比特位“0”,以便于计算机系统刚好以1个字节(8比特位)的方式来进行处理、存储和传输。


    image.png
    七、字符编码模型

    字符编码模型(Character Encoding Model),是反映字符编码系统的结构特点和各构成部分相互关系的模型框架。

    由于历史的原因,早期一般认为字符集和字符编码是同义词,并不需要进行严格区分。因此在像ASCII这样的简单字符集为代表的传统字符编码模型中,这两个概念的含义几乎是等同的。

    因为在传统字符编码模型中,基本上都是将字符集里的字符进行编号(字符编号转化为二进制数后一般不超过一个字节),然后该字符编号就是字符的编码。

    但是,由统一码(Unicode)和通用字符集(UCS)为代表的现代字符编码模型则没有直接采用ASCII这样的简单字符集的编码思路,而是采用了一个全新的编码思路。

    这个全新的编码思路将字符集与字符编码的概念更为细致地分解为了以下几个方面:

    有哪些字符;
    这些字符的编号是什么;
    这些编号如何编码成一系列逻辑层面有限大小的数字,即码元序列;
    这些逻辑层面的码元序列如何转换为(即映射为)物理层面的字节序列(即字节流);
    在某些特殊的传输环境中(比如Email中),再进一步将字节序列进行适应性编码处理。
    这几个方面作为一个整体,于是构成了现代字符编码模型。

    现代字符编码模型之所以要分解为这么几个方面,其核心思想是创建一个能够用不同方式来编码的通用字符集。注意这里的关键词:“不同方式”与“通用”。

    这意味着,同一个字符集,可以通用于不同的编码方式;也就是说,可以采用不同的编码方式来对同一个字符集进行编码。字符集与编码方式之间的关系可以是一对多的关系。

    更进一步而言,在传统字符编码模型中,字符编码方式与字符集是紧密结合在一起的;而在现代字符编码模型中,字符编码方式与字符集脱钩了。用软件工程的专业术语来说,就是将之前紧密耦合在一起的字符编码方式与字符集解耦了。

    因此,为了正确地表示这个现代字符编码模型,需要采用更多比“字符集”和“字符编码”更为精确的概念术语来描述。

    Unicode Technical Report (UTR统一码技术报告) #17《UNICODE CHARACTER ENCODING MODEL》中,现代字符编码模型分为了5个层次,并引入了更多的概念术语来描述(下面所涉及到的一些全新的概念术语,这里只做简介,更详细的解释见后文):

    • 第1层 抽象字符表ACR(Abstract Character Repertoire):明确字符的范围(即确定支持哪些字符)
    • 第2层 编号字符集CCS(Coded Character Set):用数字编号表示字符(即用数字给抽象字符表ACR中的字符进行编号)
    • 第3层 字符编码方式CEF(Character Encoding Form):将字符编号编码为逻辑上的码元序列(即逻辑字符编码)
    • 第4层 字符编码模式CES(Character Encoding Scheme):将逻辑上的码元序列映射为物理上的字节序列(即物理字符编码)
    • 第5层 传输编码语法TES(Transfer Encoding Syntax):将字节序列作进一步的适应性编码处理

    后面将分层予以简要介绍。

    (二)关键术语解释

    一、第1层 抽象字符表ACR (Abstract Character Repertoire抽象字符清单):明确字符的范围(即确定支持哪些字符)

    抽象字符集是现代编码模型的最底层,它是一个集合,通过枚举指明了所属的所有抽象字符。但是要了解抽象字符集是什么,我们首先需要了解什么是字符与抽象字符

    字符 (character, char)

    字符是指字母、数字、标点、表意文字(如汉字)、符号、或者其他文本形式的书写“原子”。
    例: a,啊,あ, α,Д等,都是抽象的字符。

    抽象字符 (Abstract Character)

    抽象字符就是抽象的字符。像a这样的字符是有形的,但在计算机中,有许多的字符是空白的,甚至是不可打印的。比如ASCII字符集中的NULL,就是一个抽象字符。
    注意\x00,\000,NULL,0 这些写法都只是这个抽象字符的某种表现形式,而不是这个抽象字符本身。

    抽象字符集 ACR (Abstract Character Repertoire)

    抽象字符集顾名思义,指的是抽象字符的集合。
    已经有了很多标准的字符集定义: Character Sets
    比如US-ASCII, UCS(Unicode), GBK这些我们耳熟能详的名字,都是(或者至少是)抽象字符集。

    US-ASCII定义了128个抽象字符的集合。GBK挑选了两万多个中日韩汉字和其他一些字符组成字符集,而UCS则尝试去容纳一切的抽象字符。它们都是抽象字符集。
    抽象字符 英文字母A同时属于US-ASCII, UCS, GBK这三个字符集。
    抽象字符 中文文字蛤不属于US-ASCII,属于GBK字符集,也属于UCS字符集。
    抽象文字 Emoji 不属于US-ASCII与GBK字符集,但属于UCS字符集。

    集合的一个重要特性,就是无序性。
    集合中的元素都是无序的,所以抽象字符集中的字符都是无序的。

    抽象字符集与python中的set的概念类似:
    例如:我可以自己定义一个字符的集合,叫这个集合为haha字符集。
    haha_acr = { 'a', '吼', 'あ', ' α', 'Д' }

    大家觉得抽象字符集这个名字太啰嗦,所以有时候直接叫它字符集。

    最后需要注意一点的是,抽象字符集也是有开放与封闭之分的。
    字符表可以是封闭的(即字符范围是固定的),即除非创建一个新的标准,否则不允许添加新的字符,比如ASCII字符表和ISO/IEC 8859系列都是这样的例子;字符表也可以是开放的(即字符范围是不固定的),即允许不断添加新的字符,比如Unicode字符表和一定程度上Code Page代码页(代码页后文会有详细解释)是这方面的例子。

    二、第2层 编号字符集 CCS(Coded Character Set):用数字编号表示字符(即用数字给字符编号)

    【注:一般将“Coded Character Set”翻译为“编码字符集”或“已编码字符集”,但这里的“编码”二字容易导致与后文的“编码方式”及“编码模式”中的“编码”二字混淆,带来理解上的困扰,因此觉得翻译为“编号字符集”为宜。】

    前面讲了,抽象字符表里的字符是没有编排顺序的,但无序的抽象字符表只能判断某个字符是否属于某个字符表,却无法方便地引用、指称该字符表中的某个特定字符。

    为了更方便地引用、指称字符表中的字符,就必须为抽象字符表中的每个字符进行编号。

    所谓字符编号,就是将抽象字符表ACR中的每个抽象字符(简称字符)表示为1个非负整数N或者映射到1个坐标(非负整数值对x, y),也就是将抽象字符的集合映射到一个非负整数或非负整数值对的集合,映射的结果就是编号字符集CCS。因此,字符的编号也就是字符的非负整数代码。

    例如,在一个给定的抽象字符表中,表示大写拉丁字母“A”的字符被赋予非负整数65、字符“B”是66,如此继续下去。

    由此产生了编号空间(Code Space,一般翻译为代码空间、码空间、码点空间)的概念:根据抽象字符表中抽象字符的数目,可以设定一个字符编号的上限值(该上限值往往设定为大于抽象字符表中的字符总数),从0到该上限值之间的非负整数范围就称之为编号空间。

    编号空间的描述:

    1)可以用一对非负整数来描述,例如:GB2312的汉字编号空间是94 x 94;

    2)也可以用一个非负整数来描述,例如:ISO-8859-1的编号空间是256;

    3)也可以用字符的存储单元尺寸来描述,例如:ISO-8859-1是一个8比特的编号空间(2^8=256);

    4)还可以用其子集来描述,如行、列、面(Plane平面、层面)等等。

    编号空间中的一个位置(Position)称为码点(Code Point代码点)或码位(Code Position代码位)。一个字符占用的码点所在的坐标(非负整数值对)或所代表的非负整数,就是该字符的编号,又称之为码点值(即码点编号)。

    不过,严格来讲,字符编号并不完全等同于码点编号(即码点值)。因为事实上,由于某些特殊的原因,编号字符集CSS里的码点数量要大于抽象字符表ACR中的字符数量。

    在编号字符集中,除了字符码点之外,还存在着非字符码点和保留码点,所以字符编号不如码点编号准确;但若对字符码点的码点编号称之为字符编号,倒也更为直接。

    在Unicode编码方案中,字符码点又称之为Unicode标量值(Unicode Scalar Value,感觉不如字符码点更为直观),非字符码点和保留码点详见后文介绍。

    注意,经常直接以“码点”指代“码点值”——当说到“码点”的时候,有可能是在说编号空间(即码点空间)中的一个位置,即码点;也有可能实际是在说某个码点的码点值,即码点编号。具体根据上下文而定。

    这种类似的指代非常普遍,又比如“字符集”、“字符编号”和“字符编码”这三个概念就经常相互指代。以“码点”指代“码点值”,根据上下文,倒也还不难理解;但“字符集”、“字符编号”和“字符编码”三者也经常相互指代,虽然有其历史原因,但目前的实际情况所导致的结果却是使人迷惑、让人抓狂!

    因此,所谓编号字符集,可简单理解为就是把抽象字符进行逐个编号或者说逐个映射为码点值(即码点编号)后所得到的结果。编号字符集CSS常简称为字符集。

    注意不要将编号字符集CSS和抽象字符表ACR相混淆。多个不同的编号字符集CCS可以表示同一个抽象字符表ACR;也就是说,同一个抽象字符表ACR,可以按不同的编号规则被编号为多个不同的编号字符集CCS。

    image.png
    在Unicode标准中,一个单个的抽象字符,既有可能与多个码点对应(为了与其它标准兼容,比如码点编号为U+51C9与U+F979的这两个码点实际上是同一个字符“凉”,这是为了兼容韩国字符集标准KS X 1001:1998,具体可参看Unicode的官方文档),也有可能使用一个由多个码点所组成的码点序列表示(为了表示由基本字符与组合字符组合在一起所组成的字符,比如à,由码点编号为U+0061的基本字符字母“a”和码点编号为U+0300的组合字符读音符号“̀”组成);同时,也并非每一个码点都对应于一个字符,也存在着非字符码点或保留码点。详见后文解释。

    特别注意:虽然“编号”与后文某种字符编码方式CEF中的“编码”(即码元序列,解释详见后文)以及某种字符编码模式CES中的“编码”(即字节序列,解释详见后文)存在着对应的关系,但“编号”与“编码”是截然不同的两个概念

    对字符编号的过程——即确定字符码点值的过程,跟计算机还没有直接关系,可认为是一个纯数学的问题,因为只是将字符与编号(即码点值、码点编号)一一对应起来,根本就还没有涉及到编码算法的问题(即还没有涉及到:根据指定的字符编码方式CEF对编号进行编码以形成码元序列,以及根据指定的字符编码模式CES对码元序列进一步编码以形成字节序列)。

    但这两个概念经常被混用,实实在在是让人困惑的源头,这是很令人无奈与遗憾的现实。

    三、第3层 字符编码方式CEF(Character Encoding Form字符编码形式、字符编码格式、字符编码规则):将字符编号(即码点值)编码为码元序列(即字符编码)

    在讲抽象字符表ACR时说过,不同于传统的封闭的ASCII字符表,Unicode字符表是一个现代的开放的字符表,未来可能有更多的字符加入进来(比如很多Emoji表情符就被源源不断地加入进来)。因此,Unicode编号字符集所需要的码点数量,必然是会不断增加的,相对来说是无限的。

    但计算机所能表示的整数范围却是相对有限的。比如,一个无符号单字节整型数(unsigned char, uint8)能够表示的编号只有0~0xFF,共256个;一个无符号双字节短整型数(unsigned short, uint16)的能够表示的编号只有0~0xFFFF,共65536个;而一个无符号四字节长整型数(unsigned long, uint32)能表示的码位只有0~0xFFFFFFFF,共4294967296个。

    那么问题来了:

    1)一方面,怎么通过相对有限的整型数来高可扩展地、高可适应地应对未来相对无限增长的字符数量呢?是用多个单字节整型数来间接表示,还是用一个足够大的多字节整型数来直接表示?

    2)另一方面,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五个层面在内的一整套标准方案系统,并不特指其中的某一个层面。


    image.png
    四、第4层 字符编码模式CES(Character Encoding Scheme):将码元序列映射为字节序列

    【注:一般将“Character Encoding Scheme”翻译为“字符编码方案”,但习惯上通常将某个字符编码标准或字符编码系统称之为“字符编码方案”,为避免引起理解上的困扰,应译作“字符编码模式”为宜。】

    字符编码模式CES,也称作“序列化格式”(Serialization Format),指的是将字符编号进行编码之后的码元序列映射为字节序列(即字节流)后的形式,以便经过编码后的字符能在计算机中进行处理、存储和传输。

    如果说将编号字符集的码点值(即字符编号)映射为(即编码为)码元序列的过程属于跟特定的计算机系统平台无关的逻辑意义上的编码,那么将码元序列映射为字节序列的过程就属于跟特定的计算机系统平台相关的物理意义上的编码。

    由于硬件平台与操作系统设计上的历史原因,对于UTF-16、UTF-32等采用多字节码元的编码方式而言,必须使用一个原先称之为零宽度不中断空格(ZERO WIDTH NO-BREAK SPACE)的字符(Unicode字符编号为U+FEFF)来指定字节序(Byte-Order字节顺序、位元组顺序,或称为Endianness端序)是大端序还是小端序,计算机才能够正确地进行处理、存储和传输。(什么是字节序以及其大端序、小端序,解释详见后文)

    不过,对于UTF-8这种采用单字节码元的编码方式来说,并不存在字节序问题,不需要指明字节序。因此,在各种计算机系统平台中,UTF-8编码的码元序列与字节序列都是相同的。(为什么UTF-8不存在字节序问题,解释详见后文)

    注意,由于字符编码方式CEF与字符编码模式CES中都有“编码”二字,因此,通常所说的动词编码(Encode)有可能指的是通过字符编码方式CEF将编号字符集CCS中的字符编号转变为码元序列;也有可能指的是通过字符编码模式CES将字符编码方式CEF中的码元序列转变为字节序列。

    动词解码(Decode)则反过来,当然也同样存在两种可能。

    而通常所说的名词编码(Encoding)也就相应地有可能指的是码元序列,也有可能指的是字节序列。

    因此,必须根据上下文语境来具体理解。

    对于程序员而言,通过字符编码方式CEF编码后所形成的码元序列,更多的是一种逻辑意义上的中间编码(即编码中介,属于从字符编号到字节序列的中间状态,作为将字符编号转换为字节序列的中介而存在),不是平时直接“打交道”的对象。

    而通过字符编码模式CES将码元序列进一步编码后所形成的字节序列,才是平时直接“打交道”最多的物理意义上的最终编码(虽然事实上还有第5层的传输编码语法TES所形成的编码,但这种编码毕竟仅用于某些特殊的传输环境,平时“打交道”的机会较少)。

    五、第5层 传输编码语法TES(Ttransfer Encoding Syntax):将字节序列作进一步的适应性编码处理

    由于历史的原因,在某些特殊的传输环境中,需要对上一层次的字符编码模式CES所提供的字节序列(字节流)作进一步的适应性编码处理。一般包括两种:

    1)一种是把字节序列映射到一套更受限制的值域内,以满足传输环境的限制,例如用于Email传输的Base64编码或者quoted-printable编码(可打印字符引用编码),都是把8位的字节映射为7位长的数据(Email协议设计为仅能传输7位的ASCII字符);

    2)另一种是压缩字节序列的值,如LZW或者进程长度编码等无损压缩技术。

    (这一部分内容本文不深入阐述,有兴趣者可自行查找资料。)

    六、总结一下现代字符编码模型:
    image.png

    对于Unicode这样的现代字符编码系统来说,同一个字符因多个不同的字符编码方式CEF(如UTF-8、UTF-16、UTF-32等)而具有多个不同的码元序列(Code Unit Sequence),同一个码元序列因两个不同的字符编码模式CES(大端序、小端序)而又可能具有两个不同的字节序列(Byte Sequence)。

    但这些不同的码元序列也好,字节序列也好,只要表示的是同一个字符,所对应的码点值(即码点编号、字符编号)一般都是相同的(在Unicode标准中,为了与其它标准兼容,有少数字符可能与多个码点对应)。

    好了,关键术语先解释到这里,其他术语将在后文中陆续解释。

    相关文章

      网友评论

          本文标题:字符的编码(一)

          本文链接:https://www.haomeiwen.com/subject/gdilcctx.html