JVM 字符编解码 UTF-8 UTF-16

背景

关于字符编码问题,印象深刻的有两次:

  • JNI层获取JVM中Emoji表情出错,上层看到4个字节,到JNI层拿出来成了6个字节
  • 面试中一道笔试题
byte[] a = new byte[]{(byte) 0xc6, (byte) 0xd0};
String s = new String(a);
byte[] b = s.getBytes();
问:最后a和b的内容是否相等?

这里分析下笔试中的题目。

过程

直接敲代码调试:
字符编解码
从图中看到a数组2个字节,b数组6个字节,内容不同。
但是,为什么不同?这些数字是怎么计算出来的?

首先,我知道JVM内部是以UTF-16形式存储字符,那看下内部存了什么数:
FFFD
找Stack Overflow问:
malformed data
在尝试解析输入流时碰到了畸形的数据,被替换为Unicode U+FFFD

去Unicode官网验证:
Unicode官网
确实没骗人。

再看默认解码方式:
默认解码UTF-8

Unicode使用UTF-8解码
Unicode UTF-8

Unicode 使用UTF-16编码
这里写图片描述

现在看下UTF-8的输入流数据:

byte[] a = new byte[]{(byte) 0xc6, (byte) 0xd0};

在看下内存:
输入不合法
底层在从前往后解析数据时,发现0b11000110, 0b11010000不符合UTF-8的规范:
这里写图片描述
也就是第二个字节应该是10开头,但是我们的输入第二个字节是110开头,故只能解码成0xFFFD这个Unicode,然后这个需要用UTF-16编码存放在内存中,最后我们看到了两个0xFFFD

接下来b数组里面的数据是两个0xFFFD经过UTF-16解码成Unicode数字,然后用UTF-8编码后的数据。这个转化过程照着上面的转换表格做就可以完成,算一次印象会深刻很多。

总结

//UTF-8字节流
byte[] a = new byte[]{(byte) 0xc6, (byte) 0xd0};
//解码成字符char, JVM内部采用UTF-16编码存储字符
String s = new String(a);
//UTF-16字节流编码成UTF-8字节流输出
byte[] b = s.getBytes();

三行代码对数据做了以下事情:

  1. UTF-8输入流到Unicode数字的解码
  2. Unicode到UTF-16的编码
  3. UTF-16输入流到Unicode数字的解码
  4. Unicode到UTF-8的编码的输出流

综上,Unicode是个数,可以用UTF-8/UTF-16编码传输,占用流量/空间少,需要有输入、输出流的概念,以流的思考方式去处理数据

参考

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值