來源:https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/#icomments
在計算機用使用0、1來保存數據,存儲的單位是字節(8bit/8位),每字節保存的最大數字是256,只保存英文可以,但是加上漢字就需要擴展了。
ASCII編碼
總共有128位,用一個字節的低7位表示,0-31是控制字條換行回車刪除等,32-126是可打印字符。
ISO-8859-1
ISO組織在ASCII碼基礎上又制定了一些列標準用來擴展ASCII編碼,它們是ISO-8859-1~ISO-8859-15,其中ISO-8859-1涵不蓋了大多數西歐字符。ISO-8859-1仍為單字節編碼,共能表示256個字符。
GB2312
全稱叫《信息交換用漢字編碼字符集基本集》,雙字節編碼,總范圍是A1~F7,其中A1~A9是符號區,共包含682個符號。從B0~F7是漢字區,包含6763個漢字。
GBK
全稱叫《漢字內碼擴展規范》,擴展GB2312,能表示21003個漢字,與GB2312兼容。
GB18030
全稱叫《信息交換用漢字編碼字符集》,與GB2312兼容。國家標準,但使用中并不廣泛。
UTF-16
UTF-16具體定義了Unicode(Universal Code統一碼)字符在計算機中存取方法。用兩個字節來表示任何字符,共16個bit,所以叫UTF-16。Java以UTF-16作為內存的字符存儲模式。
UTF-8
UTF-16統一采用兩個字節表示一個字符,雖然方便,但有很大一部分的字符用一個字節就可以表示的現在要用兩個字節表示,存儲空間放大了一倍。
UTF-8采用了變長技術,規則如下:
1、如果一個字節,最高位(第8位)是0,表示這是一個ASCII字條(00~7F)??梢姡蠥SCII編碼已經是UTF-8了。
2、如果一個字節,以11開頭,連續的1的個數暗示這個字符的字節數,例如:110xxxxx代表字是雙字節UTF-8字符的首字節。
3、如果一個字節,以10開始,表示它不是首字節,需要向前查找才能得到當前字符的首字節。
如下代碼:打印出編碼的16進制
public static void main(String[] args) { String test = "a 北京"; System.out.PRintln(Arrays.toString(test.getBytes())); printHex(test.getBytes()); try { byte[] iso8859 = test.getBytes("ISO-8859-1"); printHex(iso8859); byte[] gb2312 = test.getBytes("GB2312"); printHex(gb2312); byte[] gbk = test.getBytes("GBK"); printHex(gbk); byte[] utf16 = test.getBytes("UTF-16"); printHex(utf16); byte[] utf8 = test.getBytes("UTF-8"); printHex(utf8); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } public static void printHex(byte[] array){ for(byte aByte : array){ System.out.print(Integer.toHexString(aByte & 0xFF) + " "); } System.out.println(); }
輸出結果:
[97, 32, -79, -79, -66, -87]61 20 b1 b1 be a9 61 20 3f 3f 61 20 b1 b1 be a9 61 20 b1 b1 be a9 fe ff 0 61 0 20 53 17 4e ac 61 20 e5 8c 97 e4 ba ac
1。System.out.println(Arrays.toString(test.getBytes()));默認打印出系統默認編碼(GBK)的結果
97 32 -79 -79 -66 -87(其中97對應ascii中的a,32對應ascii中的空格)
2.printHex(test.getBytes()); 按16進制打印結果61 20 b1 b1 be a9
3."ISO-8859-1"編碼將[b1 b1]轉換為 3f, [be a9]轉換為3f.ISO-8859-1是單字節編碼,中文被轉化成3f的byte,也就是“?”字符。中文字符經過ISO-8859-1編碼會丟失信息,會把不認識的字符吸收掉。
4."GB2312",英文字母保存為1個字節,漢字保存為兩字節 北->[b1 b1],京 -> [be a9]
5."GBK" 編碼同GB2312
6."UTF-16",每個字母或漢字都保存為兩個字節.
[fe ff 0 61 0 20 53 17 4e ac]結果中: [fe ff]表示Big Endian。
Big Endian:假設一個字符由兩上字節來表示 0xabcd,那么在存儲時是按[ab cd] 來存儲還是按[cd ab]的順利來存儲。如果按[ab cd]來存儲,則稱為 big Endian, 如果存儲按[cd ab],則稱為 Little Endian。
00 61 為字符"a", [00 20] 為空格, [53 17]代表UTF-16中的“北”,[4e ac]為“京”
7."UTF-8"結果:61 20 e5 8c 97 e4 ba ac
其中 61的二進制為( 0110 0001)根據UTF-8的規則,首位為0,表示這是一個ASCII碼,一個字節表示,得到“a",同理[20]得到空格。
第三個字節e5二進制為(1110 1001)為111開頭,表示這是一個三字節的開始。第四個字節 8c 二進制(1000 1100)表示為一個字節延續,第五個字節 97二進制為(1001 0111)。
e5->1110 1001去除表示字節開始的前四位,得到 --> 1001,
8c-> 1000 1100 去除表示順序的前兩位,得到--> 001100,
97-> 1001 0111 去除表示順序的前兩位,得到--> 010111;
將1001, 001100, 010111組合 [1001, 0011, 0001, 0111]得到16進制[53, 17]正好與UTF-8中的“北”相對應。
同理,“e4 ba ac”代表“京”
在代碼打印16進制的方法中
System.out.print(Integer.toHexString(aByte & 0xFF) + " ");
一個Byte值與0xFF后調用Integer的toHexstring方法。原因在于若該byte值為負時,例: -79,Java的byte保存為1個字節,補碼的結果為(1011 0001,16進制 0xb1),會強轉為Java中的負數的int,java中的int保存為4字節(11111111 11111111 11111111 10110001,16進制為 0xffffffb1).
在此處與0xFF后,會將int中的前三個字節中的1改為0,這樣得到的結果就會為0xb1.
Integer中的toHexString()
Integer中的tohexString, toBinaryString,toOctalString 都會調用方法toUnsignedString( int i, int shift),參數 shift的值,toBinaryString為1,toOctalString,toHexString為4.
private static String toUnsignedString(int i, int shift) {char[] buf = new char[32];int charPos = 32;int radix = 1 << shift; //shift為4,相當于*2^4=8int mask = radix - 1; //mask=7( 0000 1111)do { // i & mask 得到i的最未端四位二進行數字,例 i=25(0001 1001), i&mask=9(1001) //digits為數組,第9個得到char[9]='9' buf[--charPos] = digits[i & mask]; // >>>為無符號位右移,高位補0. ps:25(0001 1001)向右移動4位得到1(0000 0001) // >> 為有符號位右移,若為正數,高位補0,若為負數,高位補1. i >>>= shift;} while (i != 0);return new String(buf, charPos, (32 - charPos)); }
新聞熱點
疑難解答