字符编码

从刚接触计算机理论起,就被灌输这是一台只认识0、1的机器,数字、字符串、声音、图像、视频等都可以在计算机中表示,这就是所谓的数字化吧。

大于0、1的整数表示

既然说到了,字符编码,想必已经理解了这个问题只有0、1怎么表示2、3、4这些数字呢?,表示如下

1
2
3
4
5
6
0000 -> 0
0001 -> 1
0010 -> 2
0011 -> 3
0100 -> 4
...

采用进位使用多个bit位来表示就可以了,小数的表示这里就不展开说了,有兴趣的可以从这里看。

TODO: 内存位置,

bit 单元格,内存里面茫茫一片的01..., 内存并不去区分这个01十进制1中的01,还是10进制2中的01 ,怎么区分这个交由具体的程序去做, 关于CPU位数,OS位数以及内存大小关系

内存位置16进制编号,一个网格,网格上每个都有编号,用16进制表示,0x....表示

程序指引 int a = 1; int b = 1

字符编码

进入今天的正题字符编码,同样的问题0、1怎么来表示字符呢,比如hello、你好?

抽象的字符编码层面:把一个字符“编码”到一个数字

具体的字符编码层面:把抽象层面的数字“编码”成最终的储存形式,明确是定长还是变长;定长的话是定几个字节;用变长的话有哪几种字节长度,具体如何实现;

Unicode 与 UTF-X

脑图

  • Unicode 只是符号集,规定了符号与二进制代码的关系,没有规定二进制代码应该如何存储
  • UTF-X 根据Unicode来进行存储,涉及具体的编码;
    • 最终的存储形式 定长还是变长;
    • 如果是变长有哪几种字节长度,具体如何实现;

一般来说,字符集(关系表) 与 具体实现的编码是,1对1的关系,像ASCII、GB2312(EUC-CN存储)等,但是 Unicode 与 UTF-X 是1对多的,UTF-X 有UTF-8、UTF-16、UTF-32等

Unicode 码点(code point)

  • 码点格式:表现形式U+[XX]XXXX,X为16进制数字,4-6位表示
  • 码点范围:码点范围: U+0000~U+10FFFF;两种理解思路:
    1. U+10FFFF 1*16^5 + 16^4 = 1114112
    2. U+10FFFF + 1 = U+110000个,前一个1是后一个1的16倍,(16+1) * 65536 = 1114112
  • 17个面,每个面可表示65536个符
    • BMP(Basic Multilingual Plan 基本多语言平面) BMP字符集-鸟瀚图
    • SP(Supplementary Plans 增补平面)

编码单元 (code unit)

UTF-XX就代表了多少个比特位表示一个编码单元。Unicode码点最大10FFFF最大21位

UTF-8: 8bit即单字节为1个编码单元,UTF-16: 16bit即2个字节为1个编码单元, UTF-32:32bit即4个字节为1个编码单元

定长编码 与 变长编码

定长编码:意味着这类编码使用固定长度的字节,如ASCII固定占1个字节长度、UTF-32固定占4个字节长度。特点:复杂度低,其存在的问题是定多长,定少了不够用,定多了浪费空间

变长编码:字符所占长度不固定,如UTF-8占1/2/3/4个字节长度、UTF-16占2/4个字节长度。特点:具有一定复杂度,其核心解决的问题是如何区分不同的变长字节

定长编码的读取我们好理解,一次读固定长度的字节,根据码表将二进制翻译字符显示即可;写入时根据字符翻译成二进制,不足固定长度,向前补0即可;

那变长编码的读取和写入呢?

变长编码的实现一——UTF-8

UTF-8UTF-8利用高位保留来做区分来解决上面提到的如何区分不同的变长字节问题,缺点就是减少了有效编码空间

这种编码方式类似前缀编码设计长短不等的编码,则必须是任一字符不是另一字符编码的前缀 ,就像压缩算法中应用的哈夫曼编码

UTF-8单个字节字符的字节0开头n个字节的字符高字节是n个1开头,其中开头字节就像前缀一样。由于高位不同,多字节不会包含一字节的模式,0开头的字节不会出现在多字节字符编码中,二字节的模式不会出现在三字节模式中,也不会出现在四字节模式中;三字节的模式中也不会出现在四字节模式中;

UNICODE码点范围 UTF-8 UTF-8二进制 有效编码
U+0000~U+007F 1字节 0XXXXXXX 2^7=128个(ASCII)
U+0080~U+07FF 2字节 110XXXXX 10XXXXXX 2^11=2048个
U+0800~U+FFFF 3字节 1110XXXX 10XXXXXX 10XXXXXX 2^16=65536个
U+010000~U+10FFFF 4字节 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX 2^21=2097152个
  • UTF-8多字节二进制规则:首字节前n位为1,n+1位为0;后面字节前两位为0
  • UTF-8多(n)字节有效编码位:(n*8)-(n+1+(n-1)*2)=5n+1

上面有效编码可以看出来,ASCII段占1个字节、大多数字符是占1或者3个字节的,一些生僻极其罕见的才会占4个字节

变长编码的实现二——UTF-16

UTF-16: UTF-16是使用所谓的代理区来实现。从BMP字符集-鸟瀚图可以看到D8-D9整行都是空,这块空白就是所谓的代理区(Surrogate Area)

代理区实现原理:我们来自己实现一种字符编码来体验下代理区

大多数占2个字节,生僻极其罕见的占4个字节。

其他

  • ASCII: 计算机早期应用,只在美国,33个计算机控制字符、33个英文标点字符、10个数字字符、52个大小写字母字符,33+33+10+52=128 用一个字节的后7位表示,还空闲一个高位0XXX XXXX

  • EASCII: 计算机发展至欧洲,欧洲国家128个字符不够用,利用ASCII字节空闲的最高位扩展128个.

  • GB2312: 国标,每个汉字及符号以两个字节来表示,第一个字节为“高位字节”,第二个字节为“低位字节”

  • GBK: 国标扩展

  • EUC: EUC-CN ,Extended Unix Code,是一个使用8位编码来表示字符的方法

  • BIG5: 港澳台同胞

  • 两个独立的尝试创立单一字符集的组织:

    • 国际标准化组织(ISO)
    • 由Xerox、Apple等软件制造商于1988年组成的统一码联盟
  • UNICODE: Unicode是Unicode Standard(Unicode标准),定义了码点与字符的关系

  • ISO: 国际标准化组织

  • UCS: 通用字符集(Universal Character Set),由ISO制定的ISO 10646标准;

    • UCS-4: ISO 10646标准定义了一个32位的编码形式,称作UCS-4。UTF-32和UCS4能表示的字符是相同的。
    • UCS-2: 类似UCS-4,使用16位的编码形式。UTF-16可看成是UCS-2的父集
  • BOM: Byte Order Mark,UTF-8采用字节为编码单元,没有字节序问题,常见的是UTF-16的字节序问题。

    • UTF-16LE, 小尾序,Little Endian,也叫小端序,这里的端是指末端的意思, BOM 标记 FF EE, 小的作为尾巴在后面。windows txt 另存时的Unicde 为UTF-16LE
    • UTF-16BE, 大尾序, Big Endian,也叫大端序,这里的端也是指末端的意思,BOM 标记 EE FF, 大的作为尾巴在后面。很多编码语言如java、javascript内码采用UTF-16BE
    • 另外UTF-32也有大端小端的问题