JVM字符编码问题

JVM字符编码问题

在学习张宏老师的《自己动手写Java虚拟机时》,发现了一个问题。

老师并没有解释为什么在class文件中使用Modified UTF-8,不过,距离这本书出版已有7年之久,所以我尝试去寻找到答案。

 

 

1.前置知识:

 

标准ASCII字符集

由于计算机底层是使用二进制存储,所以要将字符存储进计算机,由于当时还不需要支持中文和其他文字,只需要存储英文字母(大小写)数字,标点符号,特殊字符。就有了ASCII集。

其中,每一个字符都对应着一个 唯一的整数这个整数就叫做码点(Code Point)

ASCII集对这些需要存储的字符做了编号,总共128个字符。存储进计算机,只需要转成二进制,再补了零,让它刚好一个字节,存储进计算机用一个字节存储。

注意:因为补了零,首位都是零

 

 

GBK

为了支持中文,就有了GBK (汉字内码扩展规范,国标)汉字编码字符集,包含了2万多个汉字等字符,GBK中一个中文字符编码成两个字节的形式存储。

‘我K你’在计算机怎么存储?

两个字节存储“我”,一个字节存储“K”,两个字节存储“你”

而GBK规定:汉字的第一个字节的第一位必须是1,再因为ASCII字符集首位都是零。计算机就可以区分了

两个字节,第一个为1,剩下15位,可以表示2的15次方(32768)个汉字

 

 

Unicode字符集(统一码):

Unicode是国际组织制定的,可以容纳世界上所有文字,符号的字符集。Unicode 字符集的编码范围是 0x0000 – 0x10FFFF (最大21位), 可以容纳一百多万个字符, 每个字符都有一个独一无二的编码,也即每个字符都有一个二进制数值和它对应。

Unicode标准规定U+D800 – U+DFFF的值不对应于任何字符,称为保留区

UTF-32,4个字节表示一个字符 ,占存储空间,通信效率变低!所以就产生了UTF-8

 

 

 

UTF-8:

U(Unicode统一码)T(Transformation转换)F(Format格式)-8

UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节

  • ASCII字符集首位都是零。计算机直接区分了
  • 如果两个字节存储,则要求第一个字节以110开头,第二个字节以10开头
  • 如果三个字节存储,则要求第一个字节以1110开头,第二个字节以10开头,第三个字节以10开头,
  • 如果四个字节存储,则要求第一个字节以11110开头,第二个字节以10开头,第三个字节以10开头,第四个字节以10开头。

 

英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节。

存储方式如图,比如“我”这个汉字:25105

 

 

UTF-16:

UTF-16是以双字节为单位的Unicode编码的编码。

问题来了:Unicode 字符集的编码范围是 0x0000 – 0x10FFFF (最大21位),Java的双字节数据类型比如char(16位,双字节),无法表示Unicode全部码点。

所以就有了UTF-16

首先,码点0x0000-0xFFFF(0~65535):将十进制码点值,直接转换为2个字节长度的二进制数字,不足补零。

 

Unicode 字符集的编码范围是 0x0000 – 0x10FFFF (最大21位),而0x10000-0x10FFFF(65536~1114111)即当大于65535,就复杂了,,比如java16位的双字节数据类型完全无法表示,UTF-16使用两个双字节来表示它。

 

步骤:

  1. Unicode 字符集的编码范围是 0x10000 – 0x10FFFF (最大21位),为了更好操作,把他变为20位,即对其减去0x10000.这样范围就变为 0x00000 – 0xFFFFF
  2. 20位刚好可以分为两个10位。前10位放在一个字节称为高位代理项(high surrogate),后10位放在另一个字节称为低位代理项(low surrogate),此时代理项的范围是0x0000-0x03FF.
  3. 这时,代理项就与BMP冲突了,所以就把代理项平移到保留区(0xD800 – 0DFFF),同时,为了避免高位代理项与低位代理项冲突,就将低位代理项平移到高位代理项在保留区的后面,给高位代理项加上0xD800,平移到0xD800 – 0DBFF区;给底位代理项加上0xDC00,平移到0xDC00 – 0DFFF区

 

比如,在网站SYMBL (◕‿◕) 符号,表情符号,象形文字,文字,字母表和整个Unicode

找到的这个字符:

Unicode 码是0x1F631

码点为128561

  1. 将0x1F631减去0x10000等于F631
  2. 转换为20位比特的二进制数字,不够20位比特,则在前面补0,为0000111101  1000110001
  3. 将前10个比特单独取出,和 0xD800(1101 1000 0000 0000)相加,得到1101 1000 0011 1101
  4. 将后10个比特单独取出,和0xDC00(1101 1100 0000 0000)相加,得到1101 1110 0011 0001
  5. 最后,合并起来,得到的二进制字节就是UTF-16的编码结果,总共是4个字节(1101 1000 0011 1101 1101 1110 0011 0001)

 

  1. 区间[0xD800, 0xDFFF]中的码点, 专用于UTF-16的代理项,未定义任何字符,
  2. 高位代理项的范围是[0xD800, 0xDBFF],而低位代理项的则是[OxDC00, 0xDFFF],是不重叠的。

 

Modified UTF-8:

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

如图:

大致意思是:

此格式与标准UTF-8格式的区别如下:

  • 空字节’\u0000’以2字节格式编码,而不是1字节格式,因此编码的字符串永远不会嵌入空。
  • 只使用1字节、2字节和3字节格式。
  • 补充字符以代理对的形式表示。

 

总结:

  • MUTF-8编码在0x10000-0x10FFFF(65536~1114111)与UTF-8一致,除了’\u0000′
  • 对于空字节’\u0000’,在UTF-8直接使用1个字节去存储(0000 0000),而MUTF-8会使用2个字节去存储,最后存储的值为0xC080(1100 0000 1000 0000)。也就是会被编码成2字节:0xC0、0x80;
  • 而在0x10000-0x10FFFF(65536~1114111)中,以三个字节存储,要求第一个字节以1110开头,后面的4个比特位用来存放UTF-16编码的高4位。第二个字节以10开头,后面6个比特位用来存放UTF-16编码的中间6位。第三个字节以10开头,后面6个比特位用来存放UTF-16编码的最低6位。

 

2.解决问题:

问题的答案终于来了:

(1)标志字符串结尾

MUTF-8对数值0做了特殊处理,0被MUTF-8编码用来表示字符串结束,和C语言的字符串表示法相兼容,保证了在已编码字符串中没有嵌入空字节,因为在C语言等语言中,单字节空字符如’\u0000’是用来标志字符串结尾的。也许这更能体现jvm是跨语言的平台了

(2)减少运算量

如.class文件modified UTF-8进行编码的,解码出来的码元是UTF-16编码出来的2字节。JVM把UTF-16编码出来的16位长的数据(2字节,操作系统用8位长的数据,即1字节)作为最小单位进行信息交换。而如java的char,java的char是2字节16位,java的内码是UTF-16。modified UTF-8可以一次编为一个UTF-16码,就减少了很多扩展字符从UTF-8转码到UTF-16时的运算量。

那么如何转换呢?

其实是一个方法readUTF。

 

扩展:

内码:程序内部使用的字符编码,如java的char,所以java的char是2字节16位,java的内码是UTF-16。

外码:程序外部交互时使用的字符编码,如class文件。java的外码是MUTF-8。

 

参考:

Chapter 4. The class File Format (oracle.com)

DataInput (Java Platform SE 8 ) (oracle.com)

unicode – Why does Java use modified UTF-8 instead of UTF-8? – Stack Overflow

Unicode、UTF-8、UTF-16、MUTF-8、Java编码扫盲 – 个人文章 – SegmentFault 思否

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇