unicode、utf-8、utf-16的理解 和 java的char类型所使用的编码

前言

说到编码,首先要搞清楚 unicode、utf-8、utf-16,至于其他的,可以暂时不管,搞懂这三个就可以无师自通了。

预备知识

对于计算机来说,任何字符其实都是一张图片,计算机把这个图片内容绘制在屏幕上,我们就能看到它了。

为了便于传输与编辑,人们事先在每个计算机上存好一套字符库,记录了每个字符的样子,其实就是字体文件。每个字符对应一个ID,这样计算机处理文字以及传输文字时,就能只处理这些ID,实际上就是一连串的整数。

由于历史原因,我们的前辈们制订了许多套编码标准(字符映射方案),其中 Unicode 便是其中一种。
举个例子:在Unicode编码方案中,汉字 对应的整数值为 19992(此为十进制表示),十六进制表示为 4E18 ,当然更规范的表示形式为U+4E18

这个Unicode编码方案是世界通用的,因此很多地方都是用的Unicode。
Unicode 的编码范围为 0x000000~0x10FFFF,大概能表示110多万个字符,这里有着全部的unicode字符表:https://unicode-table.com/

乍一看,每个字符需要用3个字节才能容纳,由于Unicode编码最大的数值才 0x10FFFF,其实只需要 21 个比特就可以了,3个字节(24位)绰绰有余了。然而实际上,使用这种等宽编码存储 会非常浪费空间,尤其是以英语为母语的国家,人家也就几十个字母和符号,每个字符却都要用3个字节存储太浪费。

UTF-8编码规则

所以针对这个问题,出现了 utf-8,utf-8使用变长编码,具体对应规则如下:

  1. 对于 U+00至U+7F,直接单字节编码,unicode值即为编码值。
    例如:大写字母A,为U+41,对应utf-8编码就是 :0100 0001
  2. 对于 U+80至U+7FF,最大数值不超过11个比特位,使用两个字节存储,形式为:110xxxxx 10xxxxxx,其中前一字节的110为固定形式,后一字节的10也是固定形式,剩下的位拼合起来刚好 11 个比特位。举例:字符Ψ,U+03A8,二进制为:0000 0011 1010 1000,只留低11位,011 1010 1000,然后把高五位01110填充到上面前一字节,低六位101000填充到后一字节,最终为:1100 1110 1010 1000

可以在Notepad++或其他文本编辑器上测试一下。
首先,编辑器编码选择utf-8。
然后,使用十六进制编辑输入 CE A8
最后,关闭十六进制编辑,看看显示的字符是什么。
在这里插入图片描述
在这里插入图片描述
3. 对于 U+800至U+FFFF,编码最大值不超过16个比特位。与双字节类似,形式为:1110xxxx 10xxxxxx 10xxxxxx,其中用于编码的位 刚好 16 个比特位,对于一个Unicode 编码,取其低 16 位,然后再由高到低依次填充。
举例:字符,U+4E27,二进制形式:‭0100 1110 0010 0111‬,填充进去,最终为:11100100 10111000 10100111,同样可以用十六进制编辑测试一下,输入E4 B8 A7,查看显示内容。
在这里插入图片描述
4. 对于 U+10000至U+10FFFF,编码最大值不超过21个比特位。与双字节类似,形式为:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ,其中用于编码的位 刚好 21 个比特位,对于一个Unicode 编码,取其低 21 位,然后再由高到低依次填充。
由于这种偏僻的字符不好显示,举例省略。

区分

单字节: 0xxxxxxx
双字节: 110xxxxx 10xxxxxx
三字节: 1110xxxx 10xxxxxx 10xxxxxx
四字节: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
可以看到,有很明显的区分特征,所以程序读取时,不会读错。

UTF-16编码规则

虽然Unicode编码范围是 0x000000 ~ 0x10FFFF,但其实并不是全部都用满了,把全世界各种乱七八糟的字符安排上,仍然有很大的空间未被使用。其中就有一些编码范围被拿来用在了特殊用途,而不是用来代表字符。
被用来当做特殊用途的编码范围是 U+D800 ~ U+DFFF。

utf-16 把字符用 双字节表示,或者用 四字节表示。
具体规则如下:(和 utf-8 完全不一样)

  1. 对于 U+0000 ~ U+FFFF,并把 U+D800 ~ U+DFFF 抠出去,直接双字节编码,即二进制位不变化。
  2. 对于 U+10000 ~ U+10FFFF,首先减去 0x10000,则区间范围由 [0x10000, 0x10FFFF] 变为 [0x00000, 0xFFFFF],此时最大值不超过 20 个比特位,取变换后的编号的低20位,将这20个比特位分为高10位低10位,那么10个比特位的变化范围仅有 [0x000, 0x3FF],高10位 加上 0xD800,低10位 加上 DC00,那么高10位 变化范围为 [0xD800, 0xDBFF],低10位 变化范围为 [0xDC00, 0xDFFF],而这两段范围恰好是 抠出去的 编码范围。

这样,每读取16位,如果恰好是 0xD800 ~ 0xDFFF,这个范围,说明它是4个字节存储的,还得往后再读取16位。再按照编码规则逆回去,就能得到真正地 Unicode 编码。

java中的字符与字符串

java中的 char 类型,j每个字符占2字节,使用的是 utf-16 存储,由于它只占2个字节,因此对于 U+10000 ~ U+10FFFF 这个范围的字符,不能使用char类型变量存储。强行用则报编译错误。不过好在 常见的汉字、日韩文字、英法字母、还有其他一些符号 都在 U+0000 ~ U+FFFF范围内。

需要注意的是,Unicode并没有把所有的汉字包含在内,~~可能嫌弃汉字太多,大部分编码全分给汉字不公平,我瞎猜的。~~还有的 汉字连字典上都没有,生活中也不可能用到。

java 中的字符串 String 是可以存储任意字符的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值