目录

Java - 字符编码

字符集

字符集是一系列字符的集合,将每个收录的字符和数字进行映射。最早的字符集是ASCII,使用一个字节进行存储字符,8位一共可以表示256个字符,而ASCII只使用了其中的128位,即0~127位,这128位里面包括了常用的英文字符以及标点符号。

一部分不使用英文的字符可以用后面的128位来表示,即128~256位,后面的128位也被称为扩展字符集。但是它表示的字符有限,对于汉字来说,用单字节无法表示完整,因此国标码采用了双字节来表示汉字(即GBK等编码)。

由于不同国家的语言编码不同,互相之间如何通过ASCII进行交流?于是就有了万国码Unicode。Unicode是一个几乎包括了世上所有字符的字符集,每个字符都有一个对应的独一无二的Unicode码,比如聊天时使用的emoji表情字符,GitHub上也可以通过 :grin: 这种写法来输出emoji字符,这个对应的是😁。

因为Unicode使用四个字节来存储,虽然编码效率高,但是会极大浪费存储空间,因此就有了对Unicode字符集进行编码解码的存储方式,如UTF-8等字符编码。字符编码其实就是对Unicode字符集的实现方式,用以约定如何用1~4个字节来存储字符。

字符编码

UTF-8

UTF-8是可变长编码,即多字节编码,在存储不同的字符时使用的字节数量是不同的。比如存储英文字母时只使用1个字节,而存储汉字时则使用3个字节。

UTF-8分为有BOM(Byte Order Marker)和无BOM的两种编码方式,现代文本编辑器在改变文件的编码时应该都看到过这两种编码。

UTF-8的好处是节省了空间,但编码效率降低了,即时间换空间。

UTF-16

UTF-16是双字节编码,属于定长编码。由于使用两个字节为一组来表示一个字符,那么就涉及到了字节顺序的问题,即大端小端的问题。这个是Mac机和PC机对字节顺序的理解不一致导致的历史遗留问题,机器在读取字节顺序时,会从低地址读取,将两个字节中的高位字节放在低地址表示,这就是大端BE,即big-endian。反之就是小端LE,即little-endian。

因此UTF-16存在三种编码:

  • UTF-16BE,使用两个字节进行存储的大端编码。Java就是使用的这个编码来存储字符,中文和英文都是两个字节(即char这个基础数据类型),所以Java是双字节编码。
  • UTF-16LE,使用两个字节进行存储的小端编码。
  • UTF-16,没有指定后缀,即不知道其是大端还是小端,所以需要用四个字节来存储,开始的两个字节表示该字节数组是大端还是小端。即FE FF表示大端,FF FE表示小端。

使用UTF-16的好处是编码效率较高,但浪费存储空间,属于用空间换时间。

不同字符集编码的英文字母和中文汉字的字节数

英文字母:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
编码:GB2312;字节数 : 1

编码:GBK;字节数 : 1

编码:GB18030;字节数 : 1

编码:ISO-8859-1;字节数 : 1

编码:UTF-8;字节数 : 1

编码:UTF-16;字节数 : 4

编码:UTF-16BE;字节数 : 2

编码:UTF-16LE;字节数 : 2

中文汉字:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
编码:GB2312;字节数 : 2

编码:GBK;字节数 : 2

编码:GB18030;字节数 : 2

编码:ISO-8859-1;字节数 : 1

编码:UTF-8;字节数 : 3

编码:UTF-16;字节数 : 4

编码:UTF-16BE;字节数 : 2

编码:UTF-16LE;字节数 : 2

参考链接

注意
本文最后更新于 February 13, 2022,文中内容可能已过时,请谨慎使用。