【数据结构】Unicode 与 UTF-8

一、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 一致

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值