Java_字符编码(2)

常见的编码

ASCII,用一个字节的低 7 位来表示 128 个字符,0 到 31 为控制字符,32 到 126 为键盘打印字符

ISO-8859-1,ASCII 的扩展,仍是单字节编码,包含 256 个字符

GB2312,国家标准总局 1980 年发布的双字节编码,包含 6763 个汉字

GBK,国家技术监督局为 Windows95 制定的汉字编码规范,包含 21003 个汉字,是 GB2312 的扩展,用 GBK 解码 GB2312 不会出现乱码

GB18030,国家强制标准,是变长多字节编码,可以有 1、2、4 个字节,与 GB2312 完全兼容


Unicode,试图将世界所有语言统一编入到一个集合中,只是一个字符集,规定了每个字符对应的二进制数,但没有规定对应二进制数的存储方式,有 UTF-8、UTF-16、UTF-32 三种具体实现

UTF-8,变长多字节编码,用 1 到 6 个字节表示一个字符,UTF-8 诞生的故事 ,编码规则只有两条,一是对于单字节字符,最高位为 0,低 7 位为字符位,所以与 ASCII 完全兼容,二是对于多字节字符,首字节的高 n 位为 1,第 n+1 位为 0,后面字节的高两位都为 10,表示当前字节不是首字节,其余剩下的都为字符位

1
2
3
4
5
6
0xxxxxxx                                               7位字符位
110xxxxx 10xxxxxx                                     11位字符位
1110xxxx 10xxxxxx 10xxxxxx                            16位字符位
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx                   21位字符位
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx          26位字符位
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 31位字符位

例如张字的 Unicode 为 101 1111 0010 0000,需要 15 位字符位,所以按照 3 个字节对应的格式来存储,从低到高依次填入,多出的字符位补 0

1
2
3
     101   111100   100000	
1110xxxx 10xxxxxx 10xxxxxx
11100101 10111100 10100000

UTF-8 的一个缺点是有很大一部分本来可以用 2 个字节表示的字符要用 3 个字节来表示,例如中文

UTF-16,变长多字节编码,用 2 个字节或 4 个字节表示一个字符,大部分中文是 2 个字节,少数不常用的是 4 个字节,所以对于英文更多的情况使用 UTF-8 只需要 1 个字节,对于中文更多的情况则使用 UTF-16 ,大部分只需要 2 个字节,比 UTF-8 要节省

UTF-32,定长 4 字节,奢侈

Java 中的编码

Java 源文件可以是任意编码的,经过 javac 的编译生成的 .class 文件将被转换为 UTF-8 (其实是Modified UTF-8 ) ,javac 有参数 -encoding 可以指定 Java 源文件使用的编码,若不指定则默认为系统的编码,例如简体中文 Windows 的编码为 GBK,javac 将假定 Java 源文件也是 GBK 编码的

但 .class 文件被 JVM 加载后,在运行时又会被转换为 UTF-16 (JVM 具体实现可以进行优化 ),最后再按照外界期望的编码方式输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Test {
  private static final String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
  private static String byteToHexString(byte b) {
    int height = ((b & 0xf0) >> 4);
    int low = (b & 0x0f);
    return hexDigits[height] + hexDigits[low];
  }
  private static String byteArrayToHexString(byte b[]) {
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < b.length; i++) {
      result.append(byteToHexString(b[i]) + " ");
    }
    return result.toString();
  }

  public static void main(String[] args) {
    String s = "ab中文"; //Unicode十六进制数为 61 62 4e2d 6587
    //String s = "\u0061\u0062\u4e2d\u6587";
    try {
      System.out.println(byteArrayToHexString(
        s.getBytes("ISO-8859-1"))); //61 62 3f 3f
      System.out.println(byteArrayToHexString(
        s.getBytes("GB2312")));     //61 62 d6 d0 ce c4
      System.out.println(byteArrayToHexString(
        s.getBytes("GBK")));        //61 62 d6 d0 ce c4
      System.out.println(byteArrayToHexString(
        s.getBytes("UTF-8")));      //61 62 e4 b8 ad e6 96 87
      System.out.println(byteArrayToHexString(
        s.getBytes("UTF-16")));     //fe ff 00 61 00 62 4e 2d 65 87
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
  }
}

以上内容是玉山整理的笔记,如有错误还请指出