1. 程式人生 > >Java_字元編碼(Unicode、UTF-8、UTF-16)

Java_字元編碼(Unicode、UTF-8、UTF-16)

首先看一下下面的程式(測試英文和中文在Unicode、UTF-8、UTF-16這三種編碼下,一個字元佔幾個位元組)

        System.out.println("a(Unicode)    :" + "a".getBytes("Unicode").length);
        System.out.println("a(Unicode)    :" + "aa".getBytes("Unicode").length);
        System.out.println("啊(Unicode)   :" + "啊".getBytes("Unicode").length);
        System.out.println("啊啊(Unicode) :" + "啊啊".getBytes("Unicode").length);
        System.out.println("");
        System.out.println("a(UTF-8)    :" + "a".getBytes("UTF-8").length);
        System.out.println("aa(UTF-8)   :" + "aa".getBytes("UTF-8").length);
        System.out.println("啊(UTF-8)   :" + "啊".getBytes("UTF-8").length);
        System.out.println("啊啊(UTF-8) :" + "啊啊".getBytes("UTF-8").length);
        System.out.println("");
        System.out.println("a(UTF-16)    :" + "a".getBytes("UTF-16").length);
        System.out.println("aa(UTF-16)   :" + "aa".getBytes("UTF-16").length);
        System.out.println("啊(UTF-16)   :" + "啊".getBytes("UTF-16").length);
        System.out.println("啊啊(UTF-16) :" + "啊啊".getBytes("UTF-16").length);

執行結果如下:

a(Unicode)      :4
a(Unicode)      :6
啊(Unicode)     :4
啊啊(Unicode) :6

a(UTF-8)      :1
aa(UTF-8)    :2
啊(UTF-8)     :3
啊啊(UTF-8) :6

a(UTF-16)      :4
aa(UTF-16)    :6
啊(UTF-16)     :4
啊啊(UTF-16) :6

可以看到UTF-8的情況:一個英文字元佔一個位元組,一個漢字佔三個位元組

但是Unicode和UTF-16的情況比較奇怪,不管是英文還是漢字,看不出佔幾個位元組。其實正確的答案是:Unicode和UTF-16的編碼下,不管是英文字元還是漢字字元,都佔兩個位元組(至於上面結果中多出來的兩個位元組是用來表示位元組順序的預設位元組)。至於為什麼,繼續往下看。

Unicode規範中推薦的標記位元組順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。

(Unicode是一種字元編碼方法,不過它是由國際組織設計,可以容納全世界所有語言文字的編碼方案。Unicode的學名是"Universal Multiple-Octet Coded Character Set",簡稱為UCS。UCS可以看作是"Unicode Character Set"的縮寫。)

在UCS編碼中有一個叫做"ZERO WIDTH NO-BREAK SPACE"的字元,它的編碼是FEFF。而FFFE在UCS中是不存在的字元,所以不應該出現在實際傳輸中。UCS規範建議在傳輸位元組流前,先傳輸字元"ZERO WIDTH NO-BREAK SPACE"。

這樣如果接收者收到FEFF,就表明這個位元組流是Big-Endian的;如果收到FFFE,就表明這個位元組流是Little-Endian的。因此字元"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。

在 Java 中直接使用Unicode 轉碼時會按照UTF-16LE 的方式拆分,並加上 BOM。 如果採用 UTF-16 拆分,在 Java 中預設採用帶有 BOM 的 UTF-16BE 拆分。

再來看一個程式:

public class Test {
    private final static char[] HEX = "0123456789abcdef".toCharArray();

    public static void main(String[] args) throws UnsupportedEncodingException {
         String str = "中國";
         String[] encoding = { "Unicode", "UnicodeBig", "UnicodeLittle", "UnicodeBigUnmarked",
         "UnicodeLittleUnmarked", "UTF-16", "UTF-16BE", "UTF-16LE" };
        
         for (int i = 0; i < encoding.length; i++) {
         System.out
         .printf("%-22s %s%n", encoding[i], bytes2HexString(str.getBytes(encoding[i])));
         }
    }

    public static String bytes2HexString(byte[] bys) {
        char[] chs = new char[bys.length * 2 + bys.length - 1];
        for (int i = 0, offset = 0; i < bys.length; i++) {
            if (i > 0) {
                chs[offset++] = ' ';
            }
            chs[offset++] = HEX[bys[i] >> 4 & 0xf];
            chs[offset++] = HEX[bys[i] & 0xf];
        }
        return new String(chs);
    }
}

執行結果如下:

Unicode                fe ff 4e 2d 56 fd
UnicodeBig             fe ff 4e 2d 56 fd
UnicodeLittle          ff fe 2d 4e fd 56
UnicodeBigUnmarked     4e 2d 56 fd
UnicodeLittleUnmarked  2d 4e fd 56
UTF-16                 fe ff 4e 2d 56 fd
UTF-16BE               4e 2d 56 fd
UTF-16LE               2d 4e fd 56

可以看到幾個不同的Unicode和UTF-16編碼的位元組順序是不同的,有的是fe ff,有的是ff fe,有的沒有。

總上所述:

Unicode和UTF-16:1個字元佔2個位元組(不管是哪國語言)

UTF-8:1個英文字元佔1個位元組,一個漢字(包括日文和韓文等)佔3個位元組

Java中的char預設採用Unicode編碼,所以Java中char佔2個位元組

另外,順便提一個知識點:1個位元組(byte)佔8位(bit)