字符编码的那些事?-1
本随笔基于 知乎作者:笨笨阿林 专栏:刨根究底学编程 专栏:目录
现代字符编码模型-一个编码系统
之所以先来认识现在字符编码系统是因为 我们一般的表述不够准确
常见的编码标准 : ASCII GB2312, GBK GB1GB18030 Unicode
先来解释 现代字符编码模型 的专有名词
抽象字符表ACR(Abstract Character Repertoire抽象字符清单) 是一个编码系统支持的所有抽象字符的集合
特点:
- 无序 字符没有编号
- 抽象 不指具体字符 指的是整体(类似 类的概念)
- 范围 可以是封闭 如:ASCII 也可以是开放的 如Unicode CodePage
编号字符集CCS(Coded Character Set) 用数字编号表示字符(即用数字给字符编号)
注:一般将“Coded Character Set”翻译为“编码字符集”或“已编码字符集”,但这里的“编码”二字容易导致与后文的“编码方式”及“编码模式”中的“编码”二字混淆,带来理解上的困扰,因此觉得翻译为“编号字符集”为宜
1.为什么编号 无序的抽象字符表只能判断某个字符是否属于某个字符表,却无法方便地引用、指称该字符表中的某个特定字符
2.怎么得到 编号字符集把抽象字符进行逐个编号或者说逐个映射为码点值(即码点编号)后所得到的结果
3.怎么编 就是将抽象字符表ACR中的每个抽象字符(简称字符)表示为1个非负整数N或者映射到1个坐标(非负整数值对x, y),
也就是将抽象字符的集合映射到一个非负整数或非负整数值对的集合,映射的结果就是编号字符集CCS
4.结果 由此产生了编号空间(Code Space,一般翻译为代码空间、码空间、码点空间)
根据抽象字符表中抽象字符的数目,可以设定一个字符编号的上限值(该上限值往往设定为大于抽象字符表中的字符总数),从0到该上限值之间的非负整数范围就称之为编号空间
编号空间的描述
-
- 可以用一对非负整数来描述,例如:GB2312的汉字编号空间是94 x 94
- 也可以用一个非负整数来描述,例如:ISO-8859-1的编号空间是256;
- 也可以用字符的存储单元尺寸来描述,例如:ISO-8859-1是一个8比特的编号空间(2^8=256);
- 还可以用其子集来描述,如行、列、面(Plane平面、层面)等等。
码点 编号空间中的一个位置(Position)称为码点(Code Point代码点)或码位(Code Position代码位)
码点值 一个字符占用的码点所在的坐标(非负整数值对)或所代表的非负整数,就是该字符的编号,又称之为码点值(即码点编号)
可以理解为 不同表现形式 (十进制 10 和 十六进制 A 都表示一个相同的数)
注意事项:有时候 码点和码点值相互 指代 需要根据上下文 判断 (原因看编号的出发点为啥编号)
字符编号并不完全等同于码点编号(即码点值) 这句应该是顺序编号123.. 一般码点的数量大于字符的数量 存在非字符码点 和 未定义字符的码点
注意于CCS 和ACR的区别 一个ACR可以对应很多的CCS
unicode标准中,一个单个的抽象字符,既有可能与多个码点对应(为了兼容) 一个码点也并非对应一个字符 非字符码点 未定义字符的码点
字符编码方式CEF(Character Encoding Form) 将字符编号(即码点值)编码转化为码元序列(即字符编码)
1.为什么要有CEF 和ASCII相比Unicod字符表中字符数量是不断增加的 而计算机固定长度位数表示的范围是有限的
那么问题来了:
1)一方面,怎么通过相对有限的整型数来高可扩展地、高可适应地应对未来相对无限增长的字符数量呢?是用多个单字节整型数来间接表示,还是用一个足够大的多字节整型数来直接表示呢?
2)另一方面,ASCII字符编码作为最早出现、已被广泛应用的编码方案,完全不兼容显然不明智,那么是直接兼容,还是间接兼容呢?
这两方面的问题需要一个综合解决方案,这就是字符编码方式CEF(Character Encoding Form)
思考:1.为啥ASCII不区分 字符编号 和 字符编码 Unicode却区分?
而在Unicode这样现代的、复杂的字符编码系统中,则必须区分字符编号与字符编码,字符编号不一定等于字符编码,字符编号与字符编码之间不一定是一个直接映射的关系,
比如UTF-8、UTF-16为间接映射,而UTF-32则为直接映射
2.字符编码方式CEF的结果就是计算机中存储的结果吗
将字符编号(即码点值)编码转化为码元序列(即字符编码)是逻辑层面的转化,与特定的计算机系统平台无关的编码方式,尚未涉及到物理层面上的、与特定的计算机系统平台相关的存储方式(第4层才涉及到)
字符编码模式CES(Character Encoding Scheme) 将码元序列映射为字节序列
1.为什么 字符编码模式CES,也称作“序列化格式”(Serialization Format),指的是将字符编号进行编码之后的码元序列映射为字节序列(即字节流)后的形式,
以便经过编码后的字符能在计算机中进行处理、存储和传输。
将码元序列映射为字节序列的过程就属于跟特定的计算机系统平台相关的物理意义上的编码过程
2.怎么做 由于硬件平台与操作系统设计上的历史原因,对于UTF-16、UTF-32等采用多字节码元的编码方式而言,在转化时需要指定端序
必须使用一个原先称之为零宽度不中断空格(ZERO WIDTH NO-BREAK SPACE)的字符(Unicode字符编号为0xFEFF)来指定
字节序(Byte-Order字节顺序、位元组顺序,或称为Endianness端序)是大端序还是小端序,计算机才能够正确地进行处理、存储和传输
UTF-8不存在端序问题
思考 Java中的codePoint是指什么?
对于程序员而言,通过字符编码方式CEF编码后所形成的码元序列,更多的是一种逻辑意义上的中间编码
而通过字符编码模式CES将码元序列进一步编码后所形成的字节序列,才是平时直接“打交道”最多的物理意义上的最终编码
传输编码语法TES(ES(Ttransfer Encoding Syntax)) 将字节序列作进一步的适应性编码处理
由于历史的原因,在某些特殊的传输环境中,需要对上一层次的字符编码模式CES所提供的字节序列(字节流)作进一步的适应性编码处理。一般包括两种:
1)一种是把字节序列映射到一套更受限制的值域内,以满足传输环境的限制,例如用于Email传输的Base64编码或者quoted-printable编码(可打印字符引用编码),都是把8位的字节映射为7位长的数据。
2)另一种是压缩字节序列的值,如LZW或者进程长度编码等无损压缩技术。
对于Unicode这样的现代字符编码系统来说,同一个字符因多个不同的字符编码方式CEF(如UTF-8、UTF-16、UTF-32等)而具有多个不同的码元序列(Code Unit Sequence),
同一个码元序列因两个不同的字符编码模式CES(大端序模式、小端序模式)而又可能具有两个不同的字节序列(Byte Sequence)。
但这些不同的码元序列也好,字节序列也好,只要表示的是同一个字符,所对应的码点值(即码点编号、字符编号)一般都是相同的(在Unicode标准中,为了与其它标准兼容,有少数字符可能与多个码点对应)。