1. 程式人生 > >Java中的UTF-8、UTF-16編碼字元所佔位元組數

Java中的UTF-8、UTF-16編碼字元所佔位元組數

前言:上一篇文章寫了關於Unicode,以及utf-8、utf-16相關知識。所以本篇博文來驗證在java環境下,字元在不同編碼下所佔的字計數。

測試程式碼如下:

package string;

public class CharByteTest {

    public static void main(String[] args) throws Exception {
        // 第二個字元為BMP之外的字元,csdn編輯器無法顯示該字元,可以在執行結果截圖中看到
        String[] strArr = {"中", "��", "a", "aa"};
        String[] charsetArr = {"gbk"
, "utf-8", "utf-16", "gb2312"}; for(String str : strArr) { System.out.println(str); for(String charset : charsetArr) { byteTest(str, charset); } System.out.println("============================"); } } public static
void byteTest(String str, String charset) throws Exception { System.out.println("編碼:" + charset + "\t所佔位元組數:" + str.getBytes(charset).length); } }

執行結果如下:

這裡寫圖片描述

在前一篇文章的基礎上,我們來分析一下執行結果。

  1. “中”字的unicode碼值為4E2D,使用UTF-8編碼佔3個位元組。由於該字元位於BMP內,所以使用UTF-16編碼應該佔2個位元組,但是執行結果為4個位元組。
  2. 第二個字元(csdn編輯器不支援該字元的顯示),該字元使用UTF-8應該佔4個位元組,執行結果正確。由於該字元位於BMP外,所以使用UTF-16編碼應該佔4個自己,但是執行結果顯示佔用了6個位元組。
  3. 英文字母a,UTF-8編碼應該和ASCII編碼相同佔用一個位元組,執行結果顯示佔用一個位元組。a在Unicode中位於BMP內,所以UTF-16編碼應該佔用4個位元組,但是執行結果缺顯示4個位元組。
  4. 按照上面的執行結果,a在UTF-8編碼下佔用1個位元組,在UTF-16編碼下佔用4個位元組。那麼猜測兩個英文字母a,即”aa“在UTF-8和UTF-16編碼下應該分別佔2個和8個位元組,但是執行結果卻和想象中的不一樣,aa在UTF-16編碼下工佔6個位元組。

執行結果好像和上一篇中講到的有點不相符啊!為什麼會出現這樣的結果的?

通過搜尋相關文章,瞭解到java的位元組碼檔案(.class)檔案採用的是UTF-8編碼,但是在java 執行時會使用UTF-16編碼。在轉碼的時候會在前面加上表示位元組順序的字元,這個字元稱為”零寬度非換行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。FEFF佔用兩個位元組,所以就解釋了為什麼java環境下英文字母a在UTF-16編碼佔3個位元組。

我們不妨將這些字元的在不同編碼下的二進位制轉換為16進位制並打印出來。
將程式碼修改如下:

package string;

public class CharByteTest {

    private static char[] HEX_CHAR = {'0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public static void main(String[] args) throws Exception {

        String[] strArr = {"中", "��", "a", "aa"};
        //String[] charsetArr = {"gbk", "utf-8", "utf-16", "gb2312"};
        String[] charsetArr = {"unicode", "utf-8", "utf-16", "utf-16BE", "utf-16LE"};
        for(String str : strArr) {
            System.out.println(str);
            for(String charset : charsetArr) {
                byteTest(str, charset);
            }
            System.out.println("============================");
        }
    }

    public static void byteTest(String str, String charset) throws Exception {
        byte[] strByte = str.getBytes(charset);
        System.out.println("編碼:" + charset 
                + "\t所佔位元組數:" + strByte.length
                + "\t16進位制:" + bytesToHexStr(strByte));
    }

    // 將byte[]用十六進位制字串
    public static String bytesToHexStr(byte[] bytes) {
        int index = 0;
        char[] hexChar = new char[bytes.length * 2];
        for(int i = 0; i < bytes.length; i++) {
            hexChar[index++] = HEX_CHAR[bytes[i] >> 4 & 0xF];
            hexChar[index++] = HEX_CHAR[bytes[i] & 0xF];
        }
        return new String(hexChar);
    }

}

執行結果:
這裡寫圖片描述