Unicode、UTF-8、UTF-16、UTF-32、ASCII、GBK、GB2312、ISO-8859-1 它们之间是什么关系?
关于这几种字符编码的关系,经过各种资料研究,总结如下图(请右键在新标签页打开查看或者下载后使用看图工具放大查看):
我们应该从历史的顺序看待这些字符编码的由来:
- ASCII(早期):早期提出的一种字符编码集,总共有 128 个字符,可以用
1
个字节 8 个比特位的低 7 位表示,也可以用十进制数字:[0-127]
每个数字表示一个字符。全部的ASCII码参考这里。 - ISO-8859-1:256 个字符编码,西欧语言对 ASCII 码的扩展。
- GB2312:汉字编码集,对 ASCII 的扩展。采用双字节表示编码范围:
A1-F7
,其中从A1-A9
是符号区,总共包含 682 个符号,6763 个汉字。 - GBK:主要针对 GB2312 的扩展,采用双字节表示编码范围:
[8140,FEFE]
(剔除XX7F
) 共 23940 个码位,共收录了 21003 个汉字。一个中文字符占2个字节。GBK 与 Unicode 是完全不同的两种编码形式,没有转换规则,互相之间的转换只能通过查表。 - Unicode(现代):对 ASCII 的扩展,一种规模巨大的字符编码集,可容纳100多万种符号,包括了地球上所有文化、所有字母和符号的编码。每一种编码对应了一个字符, Unicode 采用
4~6
位HEX
码表示一个 Unicode 码点,范围是[U+0000~U+10FFFF]
,目前仍在更新中。
由于 Unicode 只规定了表示符号的二进制代码,却没有规定如何存储这个二进制代码。所以如何存储 Unicode 有不同的实现。而 UTF-8、UTF-16、UTF-32 就是针对 Unicode 的不同存储方式的具体实现。
- UTF-8:可变长编码,可以使用 1~4 个字节表示一个符号,根据不同符号而变化。为了解决 Unicode 如何在网络上传输,互联网上使用最最广泛。在这种编码下,例如一个中文字符占 3 个字节,一个英文字符占 1 个字节,一个 Emoji 表情符号占用 4 个字节,所以说它是变长的。Unicode 和 UTF-8 编码之间需要经过转换,加密/解密。UTF-8 对 Unicode 中的第
0-127
位字符使用 1 个字节表示,和 ASCII 编码相同,从128
号开始的字符使用2
、3
、4
位字节来表示。一个字节就是一个代码单元(code unit),一个代码点(code point)可能由1-4个代码单元组成。 - UTF-16:可变长编码,一个字符使用使用 2 个或者 4 个字节来存储。在 Java 中的字符编码实现就是采用 UTF-16 编码,一个 char 占用 2 个字节,这意味着 Java 中的一个 char 只能表示
0000~FFFF
的范围的 Unicode 字符,而对于10000~10FFFF
范围的 Unicode 字符则需要用两个 char 才能表示(使用一个String)。(如果是前者范围二者不需要编码转换,如果是后者范围二者需要编码转换) - UTF-32:定长编码,一个字符采用 4 个字节存储。由于 4 字节表示的范围完全容纳了 Unicode 范围,所以 UTF-32 和 Unicode 之间不需要转换,而 UTF-8、UTF-16和 Unicode 之间都是需要经过转换的
Java 中 char
的字符编码
Java语言规范规定: Java 的 char 类型是 UTF-16 的 code unit,占用 2 字节(也就是16位),然后字符串是 UTF-16 code unit 的序列。
UTF-16 是一种存储 Unicode 编码的具体实现,Unicode 是字符集,和 ASCII 码一样,
下表是 Java 中的 char
表示不同字符集时的区别:
Java 中可以使用如下代码打印一个中文汉字的16进制表示:
char c = '中';
System.out.println(Integer.toHexString(c)); // 4e2d
输出是 4e2d
,这表明 Java 中使用char
表示一个字符确实是占 2
个字节。
而对于表情符号,它需要占用 4
个字节,所以在 Java 中你是无法使用一个char
来表示一个表情符号的,如果你尝试这么做会得到一个类似下面这样的编译器报错提示:
如何打印这个表情符号的十六进制编码呢?既然char
存不下,只能使用String
来存储了,我们可以使用 String.getBytes()
方法指定编码字符集来输出对应的bytes
数组,例如对于汉字“中”我们可以这样打印:
try {
byte[] bytes = "中".getBytes("UTF-16");
System.out.println(bytes.length);
for (byte b : bytes) {
int temp = b & 0xFF; // 取低8位,即1个字节
System.out.print(String.format("%02X", temp));
System.out.print(" ");
}
System.out.println();
} catch (UnsupportedEncodingException e) {
}
输出:
4
FE FF 4E 2D
对于表情符号类似的:
try {
byte[] bytes =