天天看点

utf-8缩略编码与普通编码的区别(jvm所使用的class文件中constant_utf8_info内存储的字符串编码)

最近在看书的时候遇到了一个新的概念,试着搜索相应结果后发现网上已有的中文资料发现基本都是讲utf-8标准编码的,却没有讲解缩略utf-8的,故而写下此篇博客。

以下是概念原文以及出处:

constant_utf8_info类型中,length值说明了这个utf-8编码的字符串长度是多少字节,它后面紧跟着的长度为length字节的连续数据是一个使用utf-8缩略编码表示的字符串。utf-8缩略编码与普通的缩略编码的区别是:从‘\u0001’到‘\u007f’使用一个字节表示(相当于1~127的ascii码),从‘\u0080’到‘\u07ff’之间所有字符的缩略编码用两个字节表示,从‘\u0800’到‘\uffff’之间的所有字符的缩略编码就按照普通utf-8编码规则使用三个字节表示。——《深入理解java虚拟机》,机械工业出版社,第二版,p170

utf-8标准编码是怎么样的?

由于网上资料很多,这里就直接粘贴过来:

UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。

如表:

1字节 0xxxxxxx

2字节 110xxxxx 10xxxxxx

3字节 1110xxxx 10xxxxxx 10xxxxxx

4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。

实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。

因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。

我们可以发现,这里面所提到的标准编码方式与书中提到的缩略编码方式没有太大区别,那么两者的区别究竟是什么?

以下内容摘自 https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8

Modified UTF-8

Implementations of the DataInput and DataOutput interfaces represent Unicode strings in a format that is a slight modification of UTF-8. (For information regarding the standard UTF-8 format, see section 3.9 Unicode Encoding Forms of The Unicode Standard, Version 4.0). Note that in the following tables, the most significant bit appears in the far left-hand column.

All characters in the range ‘\u0001’ to ‘\u007F’ are represented by a single byte:

utf-8缩略编码与普通编码的区别(jvm所使用的class文件中constant_utf8_info内存储的字符串编码)
The null character ‘\u0000’ and characters in the range ‘\u0080’ to ‘\u07FF’ are represented by a pair of bytes:
utf-8缩略编码与普通编码的区别(jvm所使用的class文件中constant_utf8_info内存储的字符串编码)
The differences between this format and the standard UTF-8 format are the following:
utf-8缩略编码与普通编码的区别(jvm所使用的class文件中constant_utf8_info内存储的字符串编码)

The null byte ‘\u0000’ is encoded in 2-byte format rather than 1-byte, so that the encoded strings never have embedded nulls.

Only the 1-byte, 2-byte, and 3-byte formats are used.

Supplementary characters are represented in the form of surrogate pairs.

可以看到这里提到的utf-8缩略编码与标准编码的前3个byte的规则基本一致,但有以下区别:

  • ‘\u0000’,空字符并非是用一个byte而是用两个byte来表示,即11000000 10000000
  • 所使用的有且仅有1-3个byte(即范围由\u0000-\uffff)
  • 补充字符用替代的字符对来表示

关于补充字符与替代的字符对

以下内容笔者只翻译了讲解所需要的部分,想了解更多请参考此处:https://docs.oracle.com/javase/6/docs/api/java/lang/Character.html#unicode

合法的unicode字符串(即unicode scalar value)范围目前是u+0000至u+10ffff

(即至多需要21个bit来表示,但替代的字符对实际是用20个bit来表示,因为使用一个char和两个char来表示,也相当于一个bit位)

而补充字符通常指大于u+ffff而小于等于u+10ffff范围内的字符,

对于这类字符,在String中通常采用一对char的方式来表示(即utf-16编码),第一个char称为high-surrogates范围(\uD800-\uDBFF,有10个可变bit位)即高位替换范围,而第二个char则称为low-surrogates范围(\uDC00-\uDFFF,有10个可变bit位)即低位替换范围。

附上范围对应的二进制

高位 D800-DBFF:1101 10xx xxxx xxxx

1101 1000 0000 0000 —— 1101 1011 1111 1111

低位 DC00-DFFF:1101 11xx xxxx xxxx

1101 1100 0000 0000 —— 1101 1111 1111 1111

如何区别补充字符?

通过surrogate code points来进行区分,即在一个char的编码范围中有两个特殊的编码指示该char是补充字符的编码。