[十]基础数据类型之Unicode编码简介

编码含义

关于编码的含义,以前也说过,计算机只能存储二进制序列
因此对于字符,保存的时候,须要进行编码为二进制,进行存储
呈现的时候,须要将二进制进行解码,转换成字符的形式
 
有不少种编码方式,好比ASCII
(American Standard Code for Information Interchange,美国信息交换标准代码)
使用一个字节进行编码,一个字节能够表示的最大值为255
很显然,对于英语和其余一些西欧语言来讲,足够了,英文字母总共才几个对吧
 
那么对于汉字呢?ASCII显然是不够用的,因此出现了GBK等以支持汉字
那么,日语 韩语呢?他们固然也搞出来一些支持他们文字的编码

Unicode诞生

因而,人们意识到他们应该提出一种标准方案来展现世界上全部语言中的全部字符,出于这个目的,Unicode诞生了
Unicode 是一种字符集,规定了符号对应的二进制代码
至于这个二进制代码如何存储则没有任何规定,也就是说它是一种编码规定
是编码字符集,而不是实际的编码方案
 
最初Unicode使用一个16位长度的二进制序列,也就是最多支持   (2的16次方-1) 65536个字符,也就是0  ~ 65535
范围是 0x0000 ~0xFFFF 
Unicode使用U+前缀, 加上编码的值,来表示Unicode中的字符编码
也就是  U+0000   ~  U+FFFF 

Unicode不够用了

显然,随着更多字符的增长,  65536 是不够用的
因而Unicode 不得不进行扩展,因而使用8位用做扩展位,形式以下
image_5bbedd5d_2d72
一个字节8位能够表示 2的8次方-1 = 256 个数,最大能够扩展为 256 *65526=16777216 个
不过也不须要那么多的字符, 仅仅使用了前面的17个
也就是
00 01 02 03 04 05 06 07 08 0A 0B 0C 0D 0E 0F 10
 
image_5bbedd5d_5f71
因而按照扩展位,划分了17个维度,这每一个维度,叫作一个平面
17个平面,编号从 0~16
每一个平面 65536个字符
17个平面,扩展后总共能够表示1114112个字符
扩展后的范围为
U+000000   ~  U+10FFFF  
 
其中对于第一个U+00  也就是U+0000到U+FFFF ,包含了最多见的字符 
被称做 基本多语言平面  Basic Multilingual Plane, BMP
其他的U+010000   ~  U+10FFFF   被称做辅助平面
至此你应该了解到了,什么是Unicode的
他就是一个具备17个平面,每一个平面能够容纳65536个字符的一张巨大的字符码表
一个字符对应一个二进制序列
基本平面中使用四位十六进制数 在零号平面之外的字符则须要使用五位或六位十六进制数了

编码方式

Unicode 没有规定字符对应的二进制码在计算机中如何存储,只是规定了他的值是多少而已
一个字符对应的实际的值,咱们称之为代码点 code point
那么一个码点实际的值怎么存储呢?
可能须要1个,可能须要2个,甚至可能须要3个或者4个字节来表示
对于计算机来讲,面对着一堆字节,他们知道到底哪一个或者哪几个是一个字符呢?
听起来可能有点迷惑,不是知道具体的值了么?怎么还不知道如何表示?

好比数字1 他的码点是1
假如我用两个字节来存储,每一个字节的前两位我当作其余的标志位, 设置为11
那么可能结果是这样子的11000000 11000001
显然,他的值并非1
编码方式只是能够保证,你的字符是按照指定的字符集进行编码的
也就是说若是你告诉我拿出来码点为1 的,我会把1100 0000 1100 0001 解析成数字1
可是并不能保证我保存的数据就是他的码点的真值,0000 0001 ,中间形式是编码方式说了算的

最直观的例子就是网络中报文的传输,都会附加本身的头信息
因此中间传输的数据并非跟你发送的数据如出一辙,中间的数据就是编码形式的存储
可是,接收端接受解析后,就是跟你发送的数据同样的,这就好像是你的字符
 
 
存储的问题就是编码方式的问题,就是表示成什么形式的问题
编码方式有UTF-8 UTF-16  UTF-32
UTF-8 是变长
UTF-32 是定长
UTF-16介于他们之间 2个字节或者4个字节
 

utf-16

UTF-16编码以16位无符号整数为单位
咱们把Unicode编码记做U  编码规则以下
若是U<0x010000, 也就是0x000000 ~ 0x00FFFF
U的UTF-16编码,  就是U对应的16位无符号整数
若是U≥0x010000    也就是0x010000 ~ 0x10FFFF
咱们先计算下 U'=U-0x010000 
能够得出来 U'  范围是 0x000000 ~ 0x0FFFFF
显然,  U'的最大值为0xFFFFF   也就是最多20个1    也就是能够被写成20个二进制位
既然是20个二进制位,那么咱们是否是能够把它拆分红两组呢?
每组10个二进制位    00  0000  0000
它能表示的范围是2的10次方=1024个 

BMP是2个字节,16位, 很显然,若是把U' 拆分红两组,每组10个二进制位的话
每个都可以保存到2个字节内

因此Unicode标准规定:基本多语言平面内,U+D800..U+DFFF的值不对应于任何字符,为代理区 ,其中又分为高代理区和低代理区

U+D800 加上10个二进制位的数值的最大值,能够获得高代理区的范围
U+D800 --->1101 10 00  0000  0000  + 0000 00 11  1111 1111 = 1101 1011 1111 1111 = 0xDBFF

下一个就是0xDBFF +1 = 0xDC00,因此低代理区从0xDC00  开始
0xDC00 加上10个二进制位的数值的最大值,能够获得低代理区的范围
0xDC00----> 1101  1100  0000  0000 + 0000 00 11  1111 1111 = 1101111111111111 = 0xDFFF
高代理区范围  U+D800 ~0xDBFF
低代理区范围   0xDC00 ~ 0xDFFF

代理区间是U+D800....U+DFFF
因此UTF-16的编码方式就是
先计算 U'=U-0x010000
而后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxx
而后分别计算高位代理和低位代理
U+D800 --->1101 10 00  0000  0000 +   0000 00 yy  yyyy yyyy = 1101 10 yy  yyyy  yyyy

0xDC00----> 1101  1100  0000  0000 + 0000 00 xx  xxxx  xxxx =  1101  11xx  xxxx  xxxx

再精简下步骤
1. 先计算 U'=U-0x010000
2. 而后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxx
3.两个值为   1101 10 yy  yyyy  yyyy  /    1101  11xx  xxxx  xxxx
 
在这种处理方式下
若是一旦读取到值为U+D800 ~0xDBFF  ,他必然是代理区间中的一部分,也就非 0号平面中的字符
并且,咱们还可以根据值判断出来,究竟是高位仍是低位,也就是第一个仍是第二个数值
只须要取出来这20位,而后再加上0x010000  这就是这个字符的码点
 
能够看得出来,对于基本平面中的字符,使用2个字节长度,16位表示,这被称之为一个代码单元
对于除了基本平面外的辅助平面,使用4个字节长度来表示,也就是两个代码单元
 
以前咱们提到过,Unicode中的一个字符的值,被称之为一个码点
显然,一个码点,可能被一个代码单元存储,也可能被两个连续的代码单元存储

UTF-32

UTF-32编码以32位无符号整数为单位
Unicode的UTF-32编码就是其对应的32位无符号整数
32位能够表示的个数为  2的32次方  为4294967296,显然绰绰有余,没什么好说的了

UTF-8

UTF-8 是目前互联网上使用最普遍的一种 Unicode 编码方式,可变长
使用 1 - 4 个字节表示一个字符,根据字符的不一样变换长度  
 
规则
能够把编码分解成两部分,headbody
head中记录须要字节的个数,使用第一个字节中1 的个数来表示
body记录真实的数据, 若是须要不止一个字节,那么body天然由多个字节组成,每一个body的前两个字节为10  其他为数据
image_5bbedd5d_aec
UTF-8编码的最大长度是4个字节,也就是最多有21个x 表示
Unicode的最大码位0x10FFFF   0001 0000 1111 1111 1111 1111也只有21位
若是一个字节足够表示
只须要一个字节便可表示,那么第一位为0  其他7位用于表示字符编码的值
看得出来明显的好处,能够兼容ASCII 
若是一个字节不够表示,根据范围选择,须要几个字节,就有几个1,而后补一个0   后面的body依次存放数据便可
想要肯定一个码点的编码
1. 查看范围,根据上表肯定格式
2.转换为对应的二进制序列
3. 替换掉x便可

字节序

根据咱们上面描述的utf8 以及utf16都有可能使用不止一个字节进行编码
其实还有不少其余数据也不只仅是一个字节进行表达
在计算机中最终都是二进制序列的形式
好比utf-16中,虽然我能够根据值肯定是否在0号平面内,仍是在扩展辅助平面的
可是,如何把一个二进制序列解析为他的值,这是一个问题
好比
0000 0001  0000 0010 假如说这是一个十六进制数
他到底表示的是0102 仍是0201? 从哪边开始解读?
人会很天然的把左边当高位,右边当低位, 可是, 计算机, 你必须对他进行说明
这就是字节序的问题
其实就是高位和低位与内存地址高低的对应关系
分为大端排序( Big endian  BE)和小端排序(Little endian  LE)
 
大端排序----高地址存储低位  低地址存储高位
小端排序----高地址存储高位  低地址存储低位
在内存中0x01020304的存储方式
 
内存地址 4000 4001 4002 4003
BE           01     02     03     04
LE           04     03     02     01
 
其实只要记住,大小端说的都是低地址
大端就是低地址存储高位
小端就是低地址存储低位
相关文章
相关标签/搜索