Unicode与UTF-8的区别简短来说:Unicode是字符集,UTF-8是编码规则。
广义的Unicode包括了字符集与UTF-8、UTF-16等多种编码规则,此处狭义的将Unicode指代为Unicode字符集。
字符集可以理解为,在Unicode中,一个字符对应着一个惟一的编号,计算机使用此编号来表示这个字符。但是计算机并不是直接使用这个编号的二进制数,主要是为了节省存储空间和宽带流量。
例子
例子:test测试
这些字符在Unicode字符集中的编号分别为:
字符 | unicode编号(16进制) |
---|---|
t | 0074 |
e | 0065 |
s | 0073 |
t | 0074 |
测 | 6d4b |
试 | 8bd5 |
现在不考虑其他,直接将他们转换为对应的二进制数。
字符 | unicode编号(16进制) | 对应的二进制数 |
---|---|---|
t | 0074 | 00000000 01110100 |
e | 0065 | 00000000 01100101 |
s | 0073 | 00000000 01110011 |
t | 0074 | 00000000 01110100 |
测 | 6d4b | 01101101 01001011 |
试 | 8bd5 | 10001011 11010101 |
可以看到,字符串test测试
对应的二进制总共占了12个字节。这样做有个问题,每个英文字母的编号只有第二个字节起作用,第一个字节都是0000 0000,浪费空间。
UTF-8
UTF-8编码规则发挥作用的时候到了,它是一种可变长度的编码规则,根据不同情况可以占用1~4个字节。
当字符编码只占一个字节时,第一个二进制都是0,0xxx xxxx,表示这个字符占一个字节,等同于标准ASCII码。
当字符编码占两个字节时,第一个字节的前两位是11,第三位是0,第二个字节的前两位是10,110x xxxx 10xx xxxx,表示这个字符总共需要占两个字节。
具体规则如下表所示:
字符编码(16进制) | 占用字节数 | UTF-8编码 |
---|---|---|
0000~007F | 一个字节 | 0xxxxxxx |
0080~07FF | 两个字节 | 110xxxxx 10xxxxxx |
0800~FFFF | 三个字节 | 1110xxxx 10xxxxxx 10xxxxxx |
1 0000~1F FFFF | 四个字节 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
使用UTF-8对上面的test测试
进行编码。
t
的Unicode编号为0074,位于0000~007F,使用一个字节编码,UTF-8编码为:0111 0100。
测
的Unicode编号为6d4b,位于0800~FFFF,使用三个字节编码。
编码过程如下:(16进制)6d4b->(2进制)0110 1101 0100 1011,将获得的二进制数,从低位开始,从右向左一位位的填入到1110 xxxx 10xx xxxx 10xx xxxx,最终获得测
的UTF-8编码为:1110 0110 1011 0101 1000 1011.
最终,每个字符的UTF-8编码如下:
字符 | Unicode编号(16进制) | UTF-8编码 |
---|---|---|
t | 0074 | 0111 0100 |
e | 0065 | 0110 0101 |
s | 0073 | 0111 0011 |
t | 0074 | 0111 0100 |
测 | 6d4b | 11100110 10110101 10001011 |
试 | 8bd5 | 11101000 10101111 10010101 |
test测试
使用UTF-8编码最终需要10个字节,比直接使用二进制存储节省了2个字节。
汉字占几个字节
这个问题的答案与采用哪种编码方式有关。以最常用的UTF-8为例,一般常用的汉字都是三个字节,其他不常用汉字用四个字节表示。
byte[] bytes = "测".getBytes("UTF-8");
System.out.println("'测'在UTF-8下占的字节数:" + bytes.length);
for (byte b : bytes) {
System.out.println(b);
}
在运行前,要将IDE的默认编码格式改为UTF-8。-Dfile.encoding=UTF-8
输出结果为:
'测'在UTF-8下占的字节数:3
-26
-75
-117
接下来分析为什么输出会是这三个数字。
"测"的Unicode编号:6d4b。
(16进制)6d4b->(Unicode)01101101 01001011->(UTF-8)11100110 10110101 10001011
在计算机中,数字都是以补码的形式存储,所以以数字形式显示时,也需要转换。关于补码的知识可以看这篇文章:补码原理
1110 0110->1(001 1001+1) = -26
1011 0101->1(100 1010+1) = -75
1000 1011->1(111 0100+1) = -117
所以"测"字最终输出显示为-26,-75,-117。
下面是UTF-8编码规则下,占四个字节的汉字。
// 四个龍,读作“zhé”
byte[] bytes = "𪚥".getBytes("UTF-8");
System.out.println(bytes.length);
// 4