關於一個字元佔多少個位元組的問題
阿新 • • 發佈:2019-02-11
首先解釋為什麼說char佔兩個位元組
Java code ?
執行上面的程式,輸出
The max value of type char is 65535.
The min value of type char is 0.
說明char的範圍從0到65535,那麼正好是兩個位元組所能表示的範圍(65535十六進位制就是0xFFFF,一個位元組能表示0~0xFF,兩個位元組能表示0~0xFFFF),所以說一個char佔兩個位元組。
那麼char的值到底是什麼呢?比如當我這樣寫char c = '放';
Java code ?
執行輸出:
The value of char 放 is 25918.
FE FF 65 3E
The unicode value of 放 is 25918.
首先你看到,這個char的值是25918,那他是什麼呢?先不管它,接著我把這個char放在一個String裡,並進行Unicode編碼,得到四個位元組FE FF 65 3E,前面兩個實際上與內容無關,是BOM,即位元組序標識,FE FF表示是Big Endian,也就是高位在前,低位在後,所以按照這個規則,講653E轉換為10進位制int,發現最後輸出25918,也就是這個字元的Unicode值是25918,所以你現在知道一個char到底儲存的是什麼了吧。
至於GBK,UTF-8,UTF-16的關係,我先拋開GBK,因為它有點特殊。
首先你要知道UTF-8和UTF-16還有UTF-32是為了方便傳輸和儲存的而產生的對Unicode字元的編碼方式。
先說UTF-8,隨著全球化Unicode流行起來,不管你做什麼,支援Unicode都將是潮流,就算你可能永遠也用不到,但這對西方國家就不太好,因為以前ASCII字符集,一個字元只需要一個位元組,而現在用Unicode一個英文字母也需要兩個位元組,如果需要傳輸和儲存,那會浪費一半的空間或流量,所以就想出了一種變長編碼方式,那就是UTF-8,它對ASCII字符集內的字元,只用一個位元組編碼,而其他字元按照一定規則進行兩、三、四位元組編碼,具體規則是:
Unicode編碼(十六進位制) UTF-8 位元組流(二進位制)
000000 - 00007F 0xxxxxxx
000080 - 0007FF 110xxxxx 10xxxxxx
000800 - 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
但這樣做一些東方國家不幹了,因為他們的字元基本都是在000800 - 00FFFF這個區間,用UTF-8反倒要多用一個位元組,總共需要三個位元組才能表示,而且用UTF-8處理他們的字元,不能直接轉換,需要做一些運算,以‘放’為例,它的Unicode碼是25918,二進位制表示是0110010100111110,如果要轉成UTF-8,首先取高四位0110,和1110拼接,組成11100110,然後中間六位010100,與10拼接構成10010100,最後低六位111110,與10拼接構成10111110,所以三個位元組是11100110 10010100 10111110,也就是十六進位制的E6 94 BE,也就是你上面寫的-26 -108 -66。可以看到這個運算量雖然不大,基本是位操作,但如果你每個字元都要這麼操作實在是有損效率,綜合這幾點考慮,於是又弄了一個UTF-16,不嚴謹地來說它等價於Unicode原生編碼,它統一採用雙位元組表示一個字元(其實有四位元組區域,但現在一般沒有用到),而由於它用多位元組表示,和Unicode一樣需要位元組序標識,你上面程式碼裡發現它得到-2, -1, 101, 62,轉為十六進位制就是FE FF 65 3E,和我第二個例項程式中相同,說明UTF-16的碼值(如表示‘放’的65 3E)和Unicode原生編碼是相同的。
UTF-32的誕生其實也不奇怪,因為UTF-16還是一個變長編碼方式,一個字元可能由兩個或四個位元組表示,有些有強迫症的人總覺得不好,所以為了他們就有了UTF-32,它統一使用四位元組表示一個字元,因為用得不多所以不詳細說了。
最後說說GBK是個什麼東西。GBK是國標擴(展)的拼音首字母,是我國在1995年制定的專門針對漢語和一些少數名族語言的編碼方式,和Unicode之間沒有一一對應的關係,也就是說Unicode中有的字元GBK不一定有,GBK有的字元Unicode也不一定有,而且GBK和Unicode中共有字元,他們的編碼值沒有一種簡單的對應關係,也就是無法通過簡單計算得到,只能通過查錶轉換。為什麼會有GBK這種奇葩呢?其實是當時Unicode還沒制定好,更沒在全球範圍內推廣,而中國人要用電腦總不可能永遠用英語吧?所以我國就自行制定了一個國標,當時是GB2312,(其實臺灣地區針對繁體還有一個Big5,但這裡就不詳述了),GB2312後來增加了很多字元,包括很多少數名族的語言,成為了一個新的編碼標準,那就是GBK。
Java code ?
1 2 3 4 5 6 |
public static void main(String[] args) {
System.out.printf( "The max value of type char is %d.%n" ,
( int )Character.MAX_VALUE);
System.out.printf( "The min value of type char is %d.%n" ,
( int )Character.MIN_VALUE);
}
|
執行上面的程式,輸出
The max value of type char is 65535.
The min value of type char is 0.
說明char的範圍從0到65535,那麼正好是兩個位元組所能表示的範圍(65535十六進位制就是0xFFFF,一個位元組能表示0~0xFF,兩個位元組能表示0~0xFFFF),所以說一個char佔兩個位元組。
那麼char的值到底是什麼呢?比如當我這樣寫char c = '放';
Java code ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public static void main(String[] args) throws Exception {
char c = '放' ;
System.out.printf( "The value of char %c is %d.%n" , c, ( int )c);
String str = String.valueOf(c);
byte [] bys = str.getBytes( "Unicode" );
for ( int i = 0 ; i < bys.length; i++) {
System.out.printf( "%X " , bys[i]);
}
System.out.println();
int unicode = (bys[ 2 ] & 0xFF ) << 8 | (bys[ 3 & 0xFF ]);
System.out.printf( "The unicode value of %c is %d.%n" , c, unicode);
}
|
執行輸出:
The value of char 放 is 25918.
FE FF 65 3E
The unicode value of 放 is 25918.
首先你看到,這個char的值是25918,那他是什麼呢?先不管它,接著我把這個char放在一個String裡,並進行Unicode編碼,得到四個位元組FE FF 65 3E,前面兩個實際上與內容無關,是BOM,即位元組序標識,FE FF表示是Big Endian,也就是高位在前,低位在後,所以按照這個規則,講653E轉換為10進位制int,發現最後輸出25918,也就是這個字元的Unicode值是25918,所以你現在知道一個char到底儲存的是什麼了吧。
至於GBK,UTF-8,UTF-16的關係,我先拋開GBK,因為它有點特殊。
首先你要知道UTF-8和UTF-16還有UTF-32是為了方便傳輸和儲存的而產生的對Unicode字元的編碼方式。
先說UTF-8,隨著全球化Unicode流行起來,不管你做什麼,支援Unicode都將是潮流,就算你可能永遠也用不到,但這對西方國家就不太好,因為以前ASCII字符集,一個字元只需要一個位元組,而現在用Unicode一個英文字母也需要兩個位元組,如果需要傳輸和儲存,那會浪費一半的空間或流量,所以就想出了一種變長編碼方式,那就是UTF-8,它對ASCII字符集內的字元,只用一個位元組編碼,而其他字元按照一定規則進行兩、三、四位元組編碼,具體規則是:
Unicode編碼(十六進位制) UTF-8 位元組流(二進位制)
000000 - 00007F 0xxxxxxx
000080 - 0007FF 110xxxxx 10xxxxxx
000800 - 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
但這樣做一些東方國家不幹了,因為他們的字元基本都是在000800 - 00FFFF這個區間,用UTF-8反倒要多用一個位元組,總共需要三個位元組才能表示,而且用UTF-8處理他們的字元,不能直接轉換,需要做一些運算,以‘放’為例,它的Unicode碼是25918,二進位制表示是0110010100111110,如果要轉成UTF-8,首先取高四位0110,和1110拼接,組成11100110,然後中間六位010100,與10拼接構成10010100,最後低六位111110,與10拼接構成10111110,所以三個位元組是11100110 10010100 10111110,也就是十六進位制的E6 94 BE,也就是你上面寫的-26 -108 -66。可以看到這個運算量雖然不大,基本是位操作,但如果你每個字元都要這麼操作實在是有損效率,綜合這幾點考慮,於是又弄了一個UTF-16,不嚴謹地來說它等價於Unicode原生編碼,它統一採用雙位元組表示一個字元(其實有四位元組區域,但現在一般沒有用到),而由於它用多位元組表示,和Unicode一樣需要位元組序標識,你上面程式碼裡發現它得到-2, -1, 101, 62,轉為十六進位制就是FE FF 65 3E,和我第二個例項程式中相同,說明UTF-16的碼值(如表示‘放’的65 3E)和Unicode原生編碼是相同的。
UTF-32的誕生其實也不奇怪,因為UTF-16還是一個變長編碼方式,一個字元可能由兩個或四個位元組表示,有些有強迫症的人總覺得不好,所以為了他們就有了UTF-32,它統一使用四位元組表示一個字元,因為用得不多所以不詳細說了。
最後說說GBK是個什麼東西。GBK是國標擴(展)的拼音首字母,是我國在1995年制定的專門針對漢語和一些少數名族語言的編碼方式,和Unicode之間沒有一一對應的關係,也就是說Unicode中有的字元GBK不一定有,GBK有的字元Unicode也不一定有,而且GBK和Unicode中共有字元,他們的編碼值沒有一種簡單的對應關係,也就是無法通過簡單計算得到,只能通過查錶轉換。為什麼會有GBK這種奇葩呢?其實是當時Unicode還沒制定好,更沒在全球範圍內推廣,而中國人要用電腦總不可能永遠用英語吧?所以我國就自行制定了一個國標,當時是GB2312,(其實臺灣地區針對繁體還有一個Big5,但這裡就不詳述了),GB2312後來增加了很多字元,包括很多少數名族的語言,成為了一個新的編碼標準,那就是GBK。