字符集与字符编码

从一个问题提及算法

为何计算字符串的长度的时候会把中文当作2个字符,英文当成一个字符来计算?数组

基本概念

字符

在计算机中,字符是一个信息单位。简单来讲就是一个汉字或者一个英文,一个标点符号。网络

字符集

顾名思义,字符集就是字符的集合。编码

字符编码

字符编码就是把字符集中的字符编码为指定集合中的某一对象,以便在计算机中存储和经过网络传递。3d

码元

是指一个已编码的文本中具备最短比特组合的单元。对于UTF-8来讲码元是8比特长。对于UTF-16来讲,码元是16比特长。对于UTF-32来讲,码元是32比特长。代理

字符编码由如下几个关键元素构成:code

  • 抽象字符表(Abstract character repertoire): 是一个系统支持的全部抽象字符的集合,简单点说就是有哪些字符。
  • 编码字符集(Coded Character Set): 是将字符集 C中每一个字符映射到1个坐标(整数值对:x, y)或者表示为1个非负整数 ,简单点说就是字符的编号。
  • 字符编码表(Character Encoding Form): 是将编码字符集中的非负整数值转换成有限比特流长度整型值(码元)的序列。简单点说就是这些编号如何编码成一系列的码元。
  • 字符编码方案(Character Encoding Scheme): 这些单元如何组成八位子节流。 好比在Unicode的场合,使用一个简单的字符来制定字节的顺序是大端序或者小端序。(UTF-8不须要专门指明字节序) 有些复杂的字符编码机制(如ISO/IEC 2022)使用控制字符转义序列在几种编码字符集或者用于减少每一个单元所用的字节数的压缩机只之间切换

首先了解一下字符编码的历史

ASCII码-1967

全称是American Standard Code for Information Interchange,译作美国信息交换标准代码。这套字符集在1967年被正式公布。orm

它一共包含128种字符,其中33个控制字符,95个可显示的字符。正好是一个字节的低7位,最高位置0。cdn

下图是一个ASCII码的表格: 对象

EASCII码 与 ISO 8859 - 1985

刚开始计算机只在美国使用,因此128个字符是够用的,可是随着科技的发展欧洲的国家也开始使用上了计算机。这时候出现了问题,128个字符不够用了。好比法语中有注音符号,因而一些欧洲国家决定利用字节中闲置的最高位编入新的符号。 这样就在ASCII码的基础上,既保证了对ASCII码的兼容性,又补充扩展了新的字符,因而就称之为Extended ASCII(扩展ASCII)码,简称EASCII码。

在EASCII码中,当第一个比特位(即字节的最高位)为0时,仍表示以前那些经常使用的ASCII字符(实际的二进制编码为0000 0000 ~ 0111 1111,对应的十进制就是0~127),而为1时就表示补充扩展的其余衍生字符(实际的二进制编码为1000 0000 ~ 1111 1111,对应的十进制就是128~255)。

EASCII的扩展部分以下图: [image:DA4EFEE5-575B-4D69-AB85-0D6A0963052E-55654-00029E64D82D4B45/18408A67-1AF3-4473-AE8A-E7E6DE1C355B.png]

可是有这样想法的国家有不少,因此还有另外一套标准就是ISO 8859。

ISO 8859 是一组字符集的总称,其下共包含了15个字符集,即ISO 8859-n ,其中n=1,2,3…11,13,14,15,16

这两种编码方式都是前128位兼容ASCII码,后128位本身定义。

GB2312 - 1981

后来计算机进入了中国,EASCII码也不够用了。要知道,汉字是世界上包含符号最多的文字。因而⌈中国国家标准总局⌋(现已改名为⌈国家标准化管理委员会⌋)在1981年,正式制订了中华人民共和国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,项目代号为GB 2312 或 GB 2312-80(GB为国标汉语拼音的首字母),此套字符集于当年的5月1日起正式实施。

GB2312编码为了不与ASCII字符编码(0~127)相冲突,规定表示一个汉字的编码(即汉字内码)的字节其值必须大于127(即字节的最高位为1),而且必须是两个大于127的字节连在一块儿来共同表示一个汉字(GB2312为双字节编码),前一字节称为高字节,后一字节称为低字节;而一个字节的值若小于127(即字节的最高位为0),天然是仍表示一个原来的ASCII字符(ASCII为单字节编码)。

所以,能够认为GB2312是对ASCII的中文扩展(即GB2312与ASCII相兼容),正如EASCII是对ASCII的欧洲文字扩展同样。

不过,很显然的是,GB2312与EASCII码的128~255这段扩展部分所表示的字符是不一样的。也就是说,GB2312与EASCII虽然都兼容ASCII,但GB2312并不兼容EASCII的扩展部分。

事实上,目前世界上除ASCII以外的其它通行的字符编码方案,基本上都兼容ASCII,但相互之间并不兼容。

GB2312中,多是出于显示上视觉美观的考虑,除汉字以外的682个字符中,甚至包括了ASCII里原本就有的数字、标点、字母等字符。也就是说,这些ASCII里原来就有的单字节编码的字符,又再编了两个字节长的GB2312编码版本。这682个字符就是常说的“全角”字符,而这682个字符中所对应的ASCII字符就被称之为“半角”字符。

Unicode

上面说了这么多种编码,每一个国家都有本身的编码标准,以便能在计算机上正确的显示本身国家的符号。可是不一样国家地区互相不能正确显示。因此有2个组织来尝试指定统一的编码标准。

  • 国际标准化组织(ISO)
  • 统一码联盟 国际标准化组织(ISO)及国际电工委员会(IEC)于1984年联合成立了ISO/IEC小组,主要用于开发统一编码项目; 而Xerox、Apple等软件制造商则于1988年组成了统一码联盟,用于开发统一码项目。 两个组织都在编写统一字符集,但后来他们发现各自在作相同的工做,同时世界上也不须要两个不兼容的字符集,因而两个组织就此合并了双方的工做成果,并为创立一个单一编码表而协同工做。

1991年,两个组织共同的工做成果Unicode 1.0正式发布,不过Unicode 1.0并不包含CJK字符(即中日韩)。

GB13000 - 1993

1993年时,包含CJK的Unicode 1.1已经发布了,因而在同一年,中国大陆制定了几乎等同于Unicode1.1的GB13000.1-93国家编码标准(简称GB13000)。是的,你没听错,中华人民共和国信息产业部把Unicode里的全部东东拿过来,而后本身从新修订发布了下,改成了国家标准GB13000。此标准等同于 ISO/IEC 10646.1:1993和Unicode 1.1。

GBK - 1995

GB2312 基本覆盖了中国大陆99%以上的使用频率,基本知足了汉字的计算机处理须要。但对人名,生僻字等并不能处理。

因而,利用GB2312未使用的码点空间,收录这些字符,于1995年又发布了《汉字内码扩展规范(GBK)》。其中K是扩展的意思。

GBK跟GB2312同样是双字节编码,然而,GBK只要求第一个字节即高字节是大于127就固定表示这是一个汉字的开始(0~127固然表示的仍是ASCII字符),再也不要求第二个字节即低字节也必须是127号以后的编码。这样,做为一样是双字节编码的GBK才能够收录比GB2312更多字符。

因此GB系列的编码方案中,一个汉字是由2个字节组成,一个英文是由一个字节组成。

接下来详细讨论一下Unicode的编码方案

USC-2 和 USC-4

顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码

UTF-8

是Unicode的一种实现方式。是一种可变长度的编码方式。它可使用1~4个字节表示一个符号,根据不一样的符号来变化字节长度。 编码规则:

  • 对于单字节符号,字节的第一位设为0,后面7位为这个符号的Unicode码
  • 对于n(n>1)字节的符号,第一个字节的前n位都设为1,第n+1位设为0,后面的字节得前2位一概设为10。剩下的二进制位所有为这个符号的Unicode码。

编码实现:

根据编码规则,同理能够推出解码规则:

举个例子: 0xxxxxxx 110xxxxx 10xxxxxx 1110xxxx 10xxxxxx 10xxxxxx 1110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-16

也是Unicode的一种实现方式。是把Unicode字符集的抽象码位映射为16位长的整数(码元)的序列。须要1~2个16位长的码元来表示,因此这是一个边长表示。

Unicode的编码空间从U+0000到U+10FFFF,共有1,112,064个码位(code point)可用来映射字符。Unicode的编码空间能够划分为17个平面(Plane),每一个平面包含2^16(65536)个码位。17个平面的码位可表示为从U+xx0000 到 U+xxFFFF,其中xx表示十六进制从00到10。第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0)。其余平面称为辅助平面(Supplementary Planes)。基本多语言平面内,从U+D800到U+DFFF之间的码位区块是永久保留不映射到Unicode字符。UTF-16就利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。

编码规则: U+0000 ~ U+D7FF 和 U+E000 ~ U+FFFF 这个范围即基本多语言平面(Basic Multilingual Plane, BMP),包含了最经常使用的字符,包含的码位范围是U+0000 到 U+FFFF,只须要一个16位的码元便可表示。

U+10000 ~ U+10FFFF 其它平面(叫作辅助平面,Supplementary Planes)中的码位,在UTF-16中被编码为一对16比特长的码元(即32bit,4Bytes),称做代理对(surrogate pair),具体方法是:

  • 码位(0x10000~0x10FFFF)减去0x10000(BMP范围),剩下20比特长的数字,范围是0..0xFFFFF。
  • 高位的10比特(数值范围为0..0x03FF)被加上0xD800获得第一个16位长的码元或称做高位代理(high surrogate),值的范围是0xD800..0xDBFF。
  • 低位的10比特的值(值的范围也是0..0x3FF)被加上0xDC00获得第二个16位长的码元或称做低位代理(low surrogate),如今值的范围是0xDC00..0xDFFF。

上述算法可理解为:辅助平面中的码位从U+10000到U+10FFFF,共计FFFFF个,即2^20=1,048,576个,须要20位来表示。若是用两个16位长的整数组成的序列来表示,第一个整数(称为前导代理)要容纳上述20位的前10位,第二个整数(称为后尾代理)容纳上述20位的后10位。还要能根据16位整数的值直接判明属于前导整数代理的值的范围(2^10=1024),仍是后尾整数代理的值的范围(也是2^10=1024)。所以,须要在基本多语言平面中保留不对应于Unicode字符的2048个码位,就足以容纳前导代理与后尾代理所须要的编码空间。这对于基本多语言平面总计65536个码位来讲,仅占3.125%.

编码规则实现:

根据UTF-16的编码规则,同理能够获得解码规则,下图是从Swift源码中截取的UTF-16的解码代码: [image:69D26150-1CDC-4E9D-B94E-916136C27353-55654-0002535098FC388A/46208888-62B3-4137-95AE-A697AFD5B43A.png]

举个例子: 例如U+10437编码(𐐷)

  • 0x10437减去0x10000,结果为0x00437,二进制为0000 0000 0100 0011 0111。
  • 分区它的上10位值和下10位值(使用二进制):0000000001 and 0000110111。
  • 添加0xD800到上值,以造成高位:0xD800 + 0x0001 = 0xD801。
  • 添加0xDC00到下值,以造成低位:0xDC00 + 0x0037 = 0xDC37。

UTF-16比起UTF-8,好处在于大部分字符都以固定长度(2字节)存储,可是UTF-16没法兼容ASCII码。

思考问题:

一、Unicode与UTF-8 16 有什么区别?

二、为何UTF-8不须要专门指明字节序?

三、为何计算字符个数会把英文当成一个中文当成两个?

四、咱们常说的Unicode编码指的是什么?