前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MUTF-8(Modified UTF-8)

MUTF-8(Modified UTF-8)

作者头像
longzeqiu
发布2019-11-18 23:05:20
1.7K1
发布2019-11-18 23:05:20
举报
文章被收录于专栏:Android小知识Android小知识

内容来自网络

在Android应用程序的Dex文件中,所有的字符串都是使用一种叫做MUTF-8(Modified UTF-8)的编码格式进行编码的。

所谓的MUTF-8编码,其实是对UTF-16字符编码的再编码。

具体的实现可以查看MUTF-8编码的代码(代码位于libcore\dex\src\main\java\com\android\dex\Mutf8.java中): public final class Mutf8 { ... public static void encode(byte[] dst, int offset, String s) { final int length = s.length(); for (int i = 0; i < length; i++) { char ch = s.charAt(i); if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. dst[offset++] = (byte) ch; } else if (ch <= 2047) { dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6))); dst[offset++] = (byte) (0x80 | (0x3f & ch)); } else { dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12))); dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6))); dst[offset++] = (byte) (0x80 | (0x3f & ch)); } } } ... }

如果UTF-16编码的字符,其值小于等于0x7F(127)的话,则MUTF-8直接用一个字节对其编码。这时,MUTF-8编码是完全和ASCII码兼容的。也就是说,如果字符串只使用了常用的一些可见字符的话,那么MUTF-8编码就基本上退化成了ASCII码。这里还有一个特例,如果UTF-16编码字符的值为0的话,MUTF-8编码将用两个字节来表示,而不是一个字节,因此要判断一下编码值非0。

所以,对于UTF-16编码字符的数值范围在0x1~0x7F之间的情况,MUTF-8编码格式如下:

因为对数值0做了特殊处理,所以经过MUTF-8编码后的值不可能为0(实际上0被MUTF-8编码用来表示字符串结束,和C语言的字符串表示法相兼容)。

接下来,代码要处理的情况是,UTF-16编码字符的数值范围在0x80~0x7FF之间的情况,当然还要包括0x0这种情况。

在这些情况下,MUTF-8编码将使用两个字节。对于第一个字节,前三个比特位是110,后面的5个比特位用来存放UTF-16编码字符数值的高5位。而对于第二个字节,前两个比特位是10,后面6个比特位用来存放UTF-16编码字符数值的低6位。对于数值为0x0的这种特殊情况,其MUTF-8编码后的值为0xC0和0x80。大致的编码格式如下图:

最后,如果UTF-16编码字符的数值范围在0x80~0xFFFF之间的话,MUTF-8将使用三个字节对其进行编码。

对于第一个字节,前四个比特位是1110,后面的4个比特位用来存放UTF-16编码字符数值的高4位。对于第二个字节,前两个比特位是10,后面6个比特位用来存放UTF-16编码字符数值的中间6位。而对于第三个字节,前两个比特位仍然是10,后面6个比特位用来存放UTF-16编码字符数值的最低6位。大致的编码格式如下:

在Android的官方Dex文件格式的文档中,对MUTF-8编码有如下描述,总结的很到位:

1)MUTF-8使用1到3个字节对UTF-16字符进行编码;

2)对于数值为0的情况,使用两个字节对其进行编码(编码后的值为0xC0和0x80);

3)采用类似于C语言中的空字符串(NULL,单字节数值为0)作为字符串结尾的标志;

4)对于UTF-16码点范围在U+10000到U+10FFFF的情况(补充字符),数值对中的每一个数值采用3字节对其编码。也就是说,对于这种情况,表示一个字符总共需要使用6个字节。

前面三点很好理解,对于第四点,理解起来有点困难,这里特别说明一下。

大家知道UTF-16使用16位来对字符进行编码,那么其取值范围就应该是0x0到0xFFFF,这已经可以表示很多字符了。但是,世界太大了,要表示的字符太多了,最终发现16位不够用了。那怎么办呢,只能继续扩展,将取值范围又向上扩展,从0x10000到0x10FFFF,称作扩展字符。这些扩展字符的值,显然不能再用16位来表示了,那就用两个16位值来表示。对于这种表示一个扩展字符使用两个16位数值的情况,UTF-16称作代替数值对(Surrogate Pair),其编码规则如下:

1)先将UTF-16补充码的数值减去0x10000;

2)将减掉之后的数值分为两个10比特的数值,假设高10位的值表示为Vh,低10位的值表示为Vl;

3)对于数值对中第一个16位的双字节来说,用0xD800加上高10位的值Vh;

4)对于数值对中第二个16位的双字节来说,用0xDC00加上低10位的值Vl。

具体的码表如下:

这里举个例子,假设要编码的UTF-16编码的数值为U+10437,编码步骤如下:

1)将数值将去0x10000,0x10437-0x10000=0x437;

2)0x437的二进制表示是0000 0000 0100 0011 0111,所以高10位是0000000001(也就是0x1),而低10位是0000110111(也就是0x37);

3)第一个16位双字节的值是0xD800+0x1=0xD801;

4)第二个16位双字节的值是0xDC00+0x37=0xDC37。

所以,UTF-16编码数值为U+10437的扩展字符,最终被UTF-16编码成0xD801和0xDC37。

还要注意一点,由于0xD800到0xDFFF都被UTF-16用来编码扩展字符了,所以这段范围内的数值会被UTF-16保留下来,不能表示其它任何字符了。

经过上面的解释,对于第四点就非常好理解了。由于UTF-16的扩展编码的两个16位数值的取值范围是在0xD800到0xDFFF,肯定是大于0x7FF的,因此处在MUTF-8编码的第三种情况下。所以,数值对中的每一个16位的值,MUTF-8都会使用3个字节对其进行编码。由于每个UTF-16的补充字符都需要用两个16位的值对来表示,所以MUTF-8编码过后会使用6个字节。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档