一、Unicode
Unicode 是数学问题,用某一数字代表某一字符,如97代表 a 98 代表b 99 代表c
大家看过周星驰的电影,9527代表周星星,可能9526代表达叔,一样的道理。
二、转换为二进制
字母 a 对应的 十六进制为 \u0061,二进制为 01100001
汉字 “呡” 对应的 十六进制为 \u5461 二进制为01010100 01100001
其中 \u 是 十六进制的意思,跟平常编程 0x 是一个意思
三、解析
好了,这时候我什么都不管,我就直接丢给你一串 二进制的数。
01010100 01100001
你说你应该怎么解析,如果按照每8位解析一次,那就是 Ta,如果按照16位解析 就是 呡。
那我们就得和TCP/IP似的,约定一个协议,你得告诉我,你这个是按照 8位解析 还是 16位解析
即白话文如下文件:
解析协议:每8位解析
内容:01010100 01100001
这样是不是舒服很多。
计算机也是这样的,
所有编码对应的开头标志
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.
四、回到UTF-8
UTF-8 -16 -32,这些编码形式,既是将Unicode的数字转为二进制的方式,也是把二进制转为Unicode数字的方式
如:我想表达字母 ab
1、字母a ----> 十六进制 \u0061
2、字母b ----> 十六进制 \u0062
3、使用 UTF-8编码
4、。。。。(看后面)
5、。。。。(看后面)
结果
文件头:EF BB BF
文件内容:01010100 01100001
OK,我们回过头来看4 5怎么操作
4、判断\u0061 的大小区间
U+ 0000 ~ U+ 007F: 0XXXXXXX (0~127)
U+ 0080 ~ U+ 07FF: 110XXXXX 10XXXXXX (128~2047)
U+ 0800 ~ U+ FFFF: 1110XXXX 10XXXXXX 10XXXXXX (2048~65535)
U+10000 ~ U+1FFFF: 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX (65536~131071)
这里大家注意一个知识点
所有其他的UNICODE字符转化成UTF-8将需要至少2个字节。每个字节由一个换码序列开始。第一个字节由唯一的换码序列,由n位连续的1加一位0组成, 首字节连续的1的个数表示字符编码所需的字节数。
举例:110XXXXX 10XXXXXX 第一个字节两个1,代表当前字符编码需要两个字节
那么 \u0061 用UTF-8解析结果应该是什么?
Unicode转换为UTF-8时,可以将Unicode二进制从低位往高位取出二进制数字,每次取6位,前面按格式填补,不足8位用0填补。
注:Unicode转换为UTF-8需要的字节数可以根据这个规则计算:如果Unicode小于0X80(Ascii字符),则转换后为1个字节。否则转换后的字节数为Unicode二进制位数减1再除以5。
0061 < 0080,所以用一个字节表示,即标记为01100001
这时候我们验证一下转换格式
我们用汉字 “中”
简单来说,我们套用一下格式
判断 4e2d 属于区间 U+ 0800 ~ U+ FFFF: 使用格式 1110XXXX 10XXXXXX 10XXXXXX
每6位补,不足补0,
4e2d = 100111000101101
那么结果就是 11100100 10111000 10101101
但是,套用格式肯定是计算机函数,我们看一下这个函数如何实现
函数实现:
///
4e2d,这时候就判断 和 0080 的关系,4e2d>0080。我们将 4e2d转为二进制,然后位数减1再除以5
4e2d 转为 二进制 100111000101101,总计15位,那么 (15-1)/5 = 2,咦 好像不对!
是的,因为我们的二进制是不全的,这里的二进制表达需要是 8 的倍数,即
字母a,十六进制 \u0061,二进制 01100001
汉字“中”,十六进制\u4e2d,二进制 01001110 00101101
所以结果是 (16-1)/5 = 3
根据定义,如果我们需要三个字节,那么第一个字节的换码序列 中就需要有三个 1,定义中说了,1的个数代表字节数量,换码序列由 n个1 和 一个 0 组成 ,那第一个字节是 1110XXXXX,那么第二个字节和第三个字节呢,固定写法 10XXXXXX
最后的结果就是 1110XXXX 10XXXXXX 10XXXXXX
OK,那么我们看一下 4e2d 转为二进制后 01001110 00101101,怎么放到1110XXXX 10XXXXXX 10XXXXXX这个格式里面,
这里是一个比较重要的知识点
怎么就能把 01001110 00101101 放到 1110XXXX 10XXXXXX 10XXXXXX
最后得出结果 11100100 10111000 10101101
使用位运算 | ,先说理论,我再给大家写个伪代码
即,我们把 X 变为 0,即 11100000 10000000 10000000
1、把 01001110 00101101 右位移12位,然后和 11100000 做 | 运算
2、把 01001110 00101101 右位移6位,这时候我们只想要当前数据的最后六位,所以先与00111111做与运算,然后和 10000000 做 | 运算
3、把 01001110 00101101 同理,我们也只想要当前数据的最后六位,所以先与00111111做与运算,再和 10000000 做 | 运算
4、得到最后的结果 11100100 10111000 10101101
上伪代码
11100000 = 0xE0
10000000 = 0x80
num = 01001110 00101101 = 0x4e2d
char[] utf8 = char[3]
uft8[0] = 0xE0 | (num >> 12)
11100000
00000100
11100100
uft8[1] = 0x80 | ((num >> 6)&00111111)
1、01001110 00101101 >> 6 = 00000001 00111000
2、00000001 00111000 & 00111111 = 00000000 00111000
3、0x80 | 00000000 00111000 = 10000000 | 00111000 = 10111000
uft8[2] = 0x80 | (num&00111111)
1、01001110 00101101 & 00111111 = 00000000 00101101
2、0x80 | 00000000 00101101 = 10000000 | 00101101 = 10101101
结果
11100100 10111000 10101101
与 11100100 10111000 10101101 一致