编码
基础概念
位

即比特(Bit),亦称二进制位、比特位、位元、位,指二进制数中的一位,是计算机中信息表示的最小单位。 每个比特有0和1两个可能的值,除了代表数值本身之外,还可代表:
数值的正、负
两种状态,如电灯的开、关,某根导线上电压的有、无,等等
一个抽象逻辑上的是、否
字节

在计算机中,通常都会使用一连串的位(比特),称之为位串(bit string比特串)。
字节(byte),又称为位元组,是计算机中计量存储容量和传输容量的一种基本计量单位,是由连续的、固定数量的位(即比特)所组成的位串(即比特串),一般由8个位组成,即1 byte = 8 bit。习惯上用大写的B表示
现代个人计算机(PC)的存储器编址,一般是以字节为单位的,称之为按字节编址,因此字节一般也是存储器的最小存取单元以及处理器的最小寻址单位(也有按位寻址、按字寻址等等,但在个人计算机上应用不普遍,这里不讨论)。
字节作为存储器的最小存取单元以及处理器的最小寻址单位这一重要特点,跟字符编码的关系极为密切(比如,码元的单字节与多字节、字节序的大端序与小端序等,都与以字节为基础的基本数据类型密切相关)。
习惯上,按照下面的图来排列一个字节上的各个位的顺序,即按照从右到左的顺序,依次为最低位(第0位)到最高位(第7位):

代计算机的事实标准就是用8位来代表一个字节(最终形成这一事实标准除了历史原因和商业原因之外,最重要的原因应该是由于二进制的特性:2的次方计算更方便快捷)
字与字长
虽然字节是大多数现代计算机的最小存储单元和传输单元,但并不代表它是计算机可以最高效地处理的数据单位。 一般来说,计算机可以最高效地处理的数据大小,应该与其字的字长相同:
字 在计算机中,一串比特位(位串、比特串)是作为一个整体来处理或运算的,这串比特位称为一个计算机字,简称字。字通常分为若干个字节(每个字节一般是8位)
字长 字的长度,是指计算机的每个字所包含的位数。字长决定了CPU一次操作所处理的实际比特位数量的多少。字长由CPU对外数据通路的数据总线宽度决定。 计算机处理数据的速率,显然和它一次能加工的位数以及进行运算的快慢有关。如果一台计算机的字长是另一台计算机的两倍,若两台计算机的速度相同,在相同的时间内,前者能做的工作一般是后者的两倍。因此,字长与计算机的功能和用途有很大的关系,是计算机的一个重要技术指标。 在目前来讲,桌面平台的处理器字长正处于从32位向64位过渡的时期,嵌入式设备基本稳定在32位,而在某些专业领域(如高端显卡),处理器字长早已经达到了64位乃至更多的128位
字符集 字符集(Character Set、Charset),字面上的理解就是字符的集合,是一个自然语言文字系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括文字、数字、字母、音节、标点符号、图形符号等。 例如ASCII字符集,定义了128个字符;GB2312定义了7445个字符。而计算机系统中提到的字符集准确地来说,指的是已编号的字符的有序集合(但不一定是连续的)。 GBK(Guo-Biao Kuozhan)

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

字符编码 字符编码(Character Encoding),是把字符集中的字符按一定格式(形式、方式)编码为某指定集合中某一对象(比如由0和1两个数字所组成的位串模式、由0~9十个数字所组成的自然数序列、电脉冲等)的过程,亦即在字符集与指定集合两者之间建立一个对应关系(映射关系)的过程。这是信息处理的一项基础技术。 而在计算机科学中,通常以字符集来表达信息,以计算机为基础的信息处理系统则利用电子元件(硬件)的不同状态的组合来表示、存储和处理信息。 电子元件不同状态(一般是开和关或称为开和闭两种状态)的组合能代表数字系统中的数字(比如开和关代表二进制中的0和1),因此字符编码的过程也就可以理解为将字符转换映射为计算机可以接受的二进制数字的过程,其目的是为了便于字符在计算机中表示、存储、处理和传输(包括在网络中传输)。

Unicode编码
目前Unicode标准中,将字符按照一定的类别划分到0~16这17个平面(Plane层面)中,每个平面中拥有2^16 = 65536个码点,因此,目前Unicode字符集所拥有的码点总数,也就是Unicode的编号空间为17*65536=1114112

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

传统字符编码模型中,基本上都是将字符集里的字符进行编号(字符编号转化为二进制数后一般不超过一个字节),然后该字符编号就是字符的编码
由统一码(Unicode)和通用字符集(UCS)为代表的现代字符编码模型则没有直接采用ASCII这样的简单字符集的编码思路,而是采用了一个全新的编码思路 这个全新的编码思路将字符集与字符编码的概念更为细致地分解为了以下几个方面:
有哪些字符 Abstract Character Repertoire
这些字符的编号是什么 Coded Character Set
这些编号如何编码成一系列逻辑层面有限大小的数字,即码元序列 Character Encoding Form
这些逻辑层面的码元序列如何转换为(映射为)物理层面的字节流(字节序列) Character Encoding Scheme
在某些特殊的传输环境中(比如Email),再进一步将字节序列进行适应性编码处理 Ttransfer Encoding Syntax 可以采用不同的编码方式来对同一个字符集进行编码。字符集与编码方式之间的关系可以是一对多的关系。 现代字符编码模型分为了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):将字节序列作进一步的适应性编码处理
现代字符编码模型
通过字符编码方式CEF编码后所形成的码元序列,更多的是一种逻辑意义上的中间编码;
通过字符编码模式CES将码元序列进一步编码后所形成的字节序列,才是平时直接“打交道”最多的物理意义上的最终编码.
对于Unicode这样的现代字符编码系统来说,同一个字符因多个不同的字符编码方式CEF(如UTF-8、UTF-16、UTF-32等)而具有多个不同的码元序列(Code Unit Sequence),同一个码元序列因两个不同的字符编码模式CES(大端序、小端序)而又可能具有两个不同的字节序列(Byte Sequence)。
但这些不同的码元序列也好,字节序列也好,只要表示的是同一个字符,所对应的码点值(即码点编号、字符编号)一般都是相同的(在Unicode标准中,为了与其它标准兼容,有少数字符可能与多个码点对应)。
抽象字符表ACR-明确字符的范围(即确定支持哪些字符)
是一个编码系统支持的所有抽象字符的集合,可以简单理解为无序的字符集合,用于确定字符的范围,即要支持哪些字符。抽象字符表ACR的一个重要特点是字符的无序性,即其中的字符并没有编排数字顺序,当然也就没有数字编号。
编号字符集CCS(Coded Character Set)-用数字编号表示字符(即用数字给字符编号)
把抽象字符进行逐个编号或者说逐个映射为码点值(即码点编号)后所得到的结果。编号字符集CSS常简称为字符集。

一个码点可能对应1个字符或者非字符或者空
一个单个的抽象字符,既有可能与多个码点对应(比如码点编号为U+51C9与U+F979的这两个码点实际上是同一个字符“凉”)也有可能使用一个由多个码点所组成的码点序列表示(比如à,由码点编号为U+0061的基本字符字母“a”和码点编号为U+0300的组合字符读音符号“̀”组成)
import unicodedata
# 原始字符
char1 = '\u51C9' # 凉
char2 = '\uF979' # 凉
# 打印原始字符及其码点
print(f"char1: {char1}, Codepoint: {ord(char1):X}")
print(f"char2: {char2}, Codepoint: {ord(char2):X}")
# 规范化 (NFC)
normalized_char1 = unicodedata.normalize('NFC', char1)
normalized_char2 = unicodedata.normalize('NFC', char2)
# 打印规范化后的字符及其码点
print(f"Normalized char1: {normalized_char1}, Codepoint: {ord(normalized_char1):X}")
print(f"Normalized char2: {normalized_char2}, Codepoint: {ord(normalized_char2):X}")
# 检查两个字符是否相等
print(f"Are the normalized characters equal? {normalized_char1 == normalized_char2}")
在大多数情况下,当你查询汉字 “凉” 的码点时,返回的是标准码点 U+51C9,这是因为:
标准化优先:U+51C9 是 Unicode 标准中的主要码点。
规范化处理:许多系统和库(如 Python 的 unicodedata)在处理文本时会进行规范化,将兼容性字符转换为标准字符。
兼容性字符使用较少:日常使用中较少直接使用兼容性字符 U+F979,它们主要用于特定的兼容性需求。 对字符编号的过程——即确定字符码点值的过程,跟计算机还没有直接关系,可认为是一个纯数学的问题,因为只是将字符与编号(即码点值、码点编号)对应起来;根本还没涉及编码算法的问题——即根据指定的字符编码方式CEF对编号进行编码以形成码元序列,以及根据指定的字符编码模式CES对码元序列进一步编码以形成字节序列。
字符编号 将抽象字符表ACR中的中每个抽象字符(简称字符)表示为1个非负整数N或者映射到1个坐标(非负整数值对x, y),也就是将抽象字符的集合映射到一个非负整数或非负整数值对的集合,映射的结果就是编号字符集CCS。因此,字符的编号也就是字符的非负整数代码。
编号空间 根据抽象字符表中抽象字符的数目,可以设定一个字符编号的上限值(该上限值往往设定为大于抽象字符表中的字符总数),从0到该上限值之间的非负整数范围就称之为编号空间
码点(码位) 编号空间中的一个位置(Position)称为码点(Code Point代码点)或码位(Code Position代码位)。
码点值(码点编号) 一个字符占用的码点所在的坐标(非负整数值对)或所代表的非负整数,就是该字符的编号,又称之为码点值(即码点编号)
字符编码方式CEF(Character Encoding Form)-将字符编号(即码点值)编码为码元序列(即字符编码)
是将编号字符集里字符的码点值(即码点编号、字符编号)转换成或者说编码成有限比特长度的编码值(即字符编码),该编码值实际上是码元(Code Unit代码单元、编码单元)的序列(Code Unit Sequence) 在ASCII这样传统的、简单的字符编码系统中,字符编号就是字符编码,字符编号与字符编码之间是一个直接映射关系。 而在Unicode这样现代的、复杂的字符编码系统中,字符编号不一定等于字符编码,字符编号与字符编码之间不一定是一个直接映射关系,比如UTF-8、UTF-16为间接映射,而UTF-32则为直接映射。 UTF-8、UTF-16与UTF-32等就是Unicode字符集(即编号字符集)常用的字符编码方式CEF。
码元 码元可理解为字符编码方式CEF(Character Encoding Form)对码点值进行编码处理时作为一个整体来看待的最小基本单元(基本单位)。 码元某种程度上可认为对应于高级语言中的基本数据类型。而高级语言层面的基本数据类型,若要更深入一步地来讲,实质上对应于机器硬件层面(汇编语言)的数据类型byte字节、word字、dword双字等在硬件中的表达与处理机制。 码元的实质,就是机器硬件层面(汇编语言)的数据类型;不同的码元,代表着不同位数的数据类型。 数据类型有单字节与多字节之分,所以码元也有单字节与多字节之分;多字节数据类型存在着字节序的所谓大端序(Big-Endian)与小端序(Little-Endian)之分,因此多字节码元也存在着大端序与小端序之分。 最常用的码元是8位(1字节)的单字节码元,另外还有16位(2字节)和32位(4字节)两种多字节码元,三种码元对应就有了Unicode字符编号(码点值)的三种UTF编码方式: UTF-8、UTF-16、UTF-32 Unicode字符编号(码点值)的三种UTF编码方式(UTF-8、UTF-16、UTF-32)分别采用了不同的码元(BYTE、WORD、DWORD)来编码

字符编码模式CES
也称作“序列化格式”(Serialization Format),指的是将字符编号进行编码之后的码元序列映射为字节序列(即字节流),以便编码后的字符在计算机中进行处理、存储和传输。 如果说将编号字符集的码点值(即字符编号)映射(编码)为码元序列的过程属于跟特定的计算机系统平台无关的逻辑意义上的编码,那么将码元序列映射(编码)为字节序列的过程就属于跟特定的计算机系统平台相关的物理意义上的编码。
字节序 就是多字节数据(大于一个字节的数据)在计算机中存储、读取时其各个字节的排列顺序。 字节序只跟多字节的整型数据类型有关,比如int、short、long型;跟单字节的整型数据类型byte无关。 CPU一般是以字为一个整体来处理数据的,当单个数据不足一个字长时,则将多个数据“拼成”一个字再进行处理),但问题是字节才是CPU对内存寻址的最小单位以及存储和传输的最小单元。 因此,CPU在读取作为一个整体来进行处理的多字节数据类型的数据时,必须根据前后2个或2个以上的字节来解析出一个多字节数据类型的值;而且构造字节序列时也会从一个多字节数据类型的值当中产生2个或2个以上的字节。

小端序Little-Endian 低位字节(即小端字节)存放在内存的低地址,而高位字节(即大端字节)存放在内存的高地址,与人类书写习惯不一致
大端序Big-Endian 高位字节(即大端字节)存放在内存的低地址,低位字节(即小端字节)存放在内存的高地址,与人类书写习惯一致 Little Endian还是Big Endian与操作系统和CPU芯片类型都有关系。因此在一个计算机系统中,有可能同时存在大端和小端两种模式的现象 计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合比如网络传输和文件储存,几乎都是用的大端字节序。计算机处理字节序的时候,如果是大端字节序,先读到的就是高位字节,后读到的就是低位字节。小端字节序则正好相反
零宽度不中断空格(ZERO WIDTH NO-BREAK SPACE零宽度无断空白) 该字符名义上是个空格,实际上是零宽度的,即相当于是不可见也不可打印字符(平常使用较多的是ASCII空格字符,是非零宽度的,需要占用一个字符的宽度,为可见不可打印字符)。 从Unicode 3.2开始,U+FEFF只能出现在字节流的开头,且只能用于标识字节序,就如它的别名——字节序标记——所表示的意思一样;除此以外的用法已被舍弃。取而代之的是,使用U+2060来表示零宽度不中断空格 如果UTF-16编码的字节序列为大端序,则该字节序标记在字节流的开头呈现为0xFE 0xFF;若字节序列为小端序,则该字节序标记在字节流的开头呈现为0xFF 0xFE。如果UTF-32编码的字节序列为大端序,则该字节序标记在字节流的开头呈现为0x00 0x00 0xFE 0xFF;若字节序列为小端序,则该字节序标记在字节流的开头呈现为0xFF 0xFE 0x00 0x00 UTF-8编码本身没有字节序的问题,但仍然有可能会用到BOM——有时被用来标示某文本是UTF-8编码格式的文本

传输编码语法TES(Ttransfer Encoding Syntax)
由于历史的原因,在某些特殊的传输环境中,需要对上一层次的字符编码模式CES所提供的字节序列(字节流)作进一步的适应性编码处理。一般包括两种:
一种是把字节序列映射到一套更受限制的值域内,以满足传输环境的限制,例如用于Email传输的Base64编码或者quoted-printable编码(可打印字符引用编码),都是把8位的字节映射为7位长的数据(Email协议设计为仅能传输7位的ASCII字符)
另一种是压缩字节序列的值,如LZW或者进程长度编码等无损压缩技术。
内码+外码
为在计算机内表示汉字而采取统一的编码方式所形成的汉字编码叫内码。为方便汉字输入而形成的汉字编码为外码,也叫输入码。为显示输出和打印输出汉字而形成的汉字编码为字形码,也称为字模码、输出码。
计算机通过键盘输入的外码(重码时还需附加选择编号)对应于汉字内码,将汉字外码转换(即映射)为汉字内码,以实现输入汉字的目的;通过汉字内码在字模库(即字库)中找出汉字的字形码,将汉字内码转换(即映射)为汉字字形码,以实现显示输出和打印输出汉字的目的。

UTF-8编码规则
验证:
a = '一'
print(f"{ord(a):b}")
# 汉字一的unicode码点值的二进制格式为100111000000000
# 按照utf-8的编码规则,汉字为三字节编码,所以从后往前将二进制对三字节编码标识位外的位置进行填充
print(f"{0b11100100:x}")
UTF-16编码
UTF-16是变长编码方式,每个字符编码为2字节或4字节;而UCS-2是定长编码方式,每个字符编码固定为2字节。
另外,UTF-16中,大部分汉字采用两个字节编码,少量不常用汉字采用四个字节编码。

零宽字符
零宽字符(Zero-width characters)是一类特殊的字符,它们在文档中占据位置但不显示任何内容。零宽字符在文本处理中有多种用途,如格式控制、字符分割、隐藏信息等。常见的零宽字符包括:
零宽空格(Zero Width Space,ZWSP):U+200B
零宽不连字(Zero Width Non-Joiner,ZWNJ):U+200C
零宽连字(Zero Width Joiner,ZWJ):U+200D
零宽非断空格(Word Joiner,WJ):U+2060
零宽无断空间(Narrow No-Break Space):U+202F
提取零宽字符 [\u200b-\u200f\uFEFF\u202a-\u202e]
隐写术是一种将秘密信息隐藏在载体(如文本、图像、音频等)中的技术,使得非授权人员无法轻易察觉到信息的存在。使用零宽字符进行文本隐写术是一种巧妙的方法,因为零宽字符在视觉上是不可见的,但它们仍然存在于字符序列中。
将秘密信息转换为二进制
使用零宽字符编码二进制信息
将零宽字符序列嵌入载体文本中
提取隐藏信息