Java 字符集 編碼
Java默認的字符集是Unicode(占兩個字節byte,一個字節=8比特位bit)
詳解:
字符集 編碼
Unicode 是「字符集」;UTF-8 是「編碼規則」(是使用最廣的一種 Unicode 的實現方式)
字符集:為每一個字符分配一個唯一的ID(碼位)
編碼規則:將碼位轉換為字節序列的規則(用什么方式存儲)
|
英文/字節 |
中文/字節 |
Utf-8(變長) |
1 |
3 |
Utf-16 |
2 |
3-4 |
GBK |
1 |
2 |
ISO8859-1 |
1 |
1 |
Unicode |
2 |
2(標點也是) |
ASCII |
1 |
2 |
Java的處理方法:
編碼問題存在兩個方面:JVM之內和JVM之外。
1.編譯器把Java文件編譯后形成class
這里Java文件的編碼可能有多種多樣(可以為utf-8(常用)),但Java編譯器會自動將這些編碼按照Java文件的編碼格式正確讀取后產生class文件,這里的class文件編碼是Unicode編碼(具體說是UTF-16編碼)。也就是說完成了從UTF-8編碼的文件轉成與平台無關的.class文件了,把UTF-8編碼方式轉成了Unicode。一旦編譯成.class文件,就不用在乎關於我們程序源碼的什么UTF-8編碼了
因此,在Java代碼中定義一個字符串String s="漢字";
不管在編譯前java文件使用何種編碼,在編譯后成class后,他們都是一樣的----Unicode編碼表示。
2.JVM中的編碼
在JVM內部,統一使用Unicode表示,當着字符從JVM內部移動到外部時(即保存為文件系統中的一個文件內容時),就進行了編碼轉換,使用了具體的編碼方案。因此也可以說,所有的編碼轉換只發生在邊界的地方,也就是各種輸入/輸出流的起作用的地方。
JVM加載class文件讀取時候使用Unicode編碼方式正確讀取class文件,那么原來定義的String s="漢字";在內存中的表現形式是Unicode編碼。
問題
在java中,一個字符等於多少字節?
或者更詳細的問:在java中,一個英文字符等於多少字節?一個中文字符等於多少字節?
Java采用unicode來表示字符,java中的一個char是2個字節,一個中文或英文字符的unicode編碼都占2個字節,但如果采用其他編碼方式,一個字符占用的字節數則各不相同。
代碼驗證如下:
public static void main(String[] args) { String str = "測"; char x = '測'; byte[] byteStr = str.getBytes(); byte[] byteChar = charToByte(x); System.out.println("byteStr :" + byteStr.length); // byteStr :3 System.out.println("byteChar:" + byteChar.length); // byteChar:2 } // 通過移位獲取char類型的byte數組 public static byte[] charToByte(char c) { byte[] b = new byte[2]; b[0] = (byte) ((c & 0xFF00) >> 8); b[1] = (byte) (c & 0xFF); return b; }
獲取系統編碼
System.out.println("系統默認編碼:" + System.getProperty("file.encoding")); //查詢結果UTF-8 System.out.println("系統默認字符編碼:" + Charset.defaultCharset()); //查詢結果UTF-8 System.out.println("系統默認語言:" + System.getProperty("user.language")); //查詢結果zh
getBytes()方法詳解
另外解釋一下上邊使用的getBytes()方法
在Java中,String的getBytes()方法是得到一個操作系統默認的編碼格式的字節數組。這表示在不同的操作系統下,返回的東西不一樣!
1.str.getBytes(); 如果括號中不寫charset,則采用的是Sytem.getProperty("file.encoding"),即當前文件的編碼方式,
2.str.getBytes("charset");//指定charset,即將底層存儲的Unicode碼解析為charset編碼格式的字節數組方式
3.String str=new String(str.getBytes("utf-8"),"gbk")); //將已經解析出來的字節數據轉化為gbk編碼格式的字符串,在內存中即為gbk格式的字節數組轉為Unicode去交互傳遞
引申
問:
"a".getBytes("Unicode").length // 結果為 4
上邊已經說過Unicode一個字符占兩個字節,這里為什么是4字節不是2字節?
為什么Unicode 4個字節
使用for循環遍歷得到的byte數組(還是使用字符 a ):
-2 -1 0 97
發現前邊多了一個 -2 -1 ,這其實是一個字節的BOM標志。
UNICODE 是一種字符集,在 Java 中直接使用 Unicode 轉碼時會按照 UTF-16LE 的方式拆分,由於 UTF-16 分為 UTF-16LE 和 UTF-16BE,也就是小端序和大端序,因此在網絡傳過程中,無法判斷是 LE 還是 BE 序的,因此需要加上一個額外的字節序 BOM 頭。BOM 頭的字符是一個特殊的字符,其 Unicode 編碼為 U+FEFF,字符名為“ZERO WIDTH NON-BREAKING SPACE”,根據 RFC2781 3.2 節規定,開頭兩個字節為 FE FF 的稱為Big-Endian,開頭為 FF FE 的稱為 Little-Endian。
關於utf-16的解釋:utf-16的方式包含2種字節序,Big Endian字節序和Little Endian字節序:
UTF-16 Big Endian:FEFF (沒有含義在UCS-2中),其中FEFF為標示碼
UTF-16 Little Endian:FFFE (沒有含義在UCS-2中),java默認選擇Little Endian字節序
因此,你直接使用 Unicode 轉換字節的話,也就是按 UTF-16LE 方式進行解碼,會額外地加上 BOM 的兩個字節 FF FE。
解決辦法:
可以使用UnicodeBigUnmarked編碼
"a".getBytes("UnicodeBigUnmarked").length // 結果為2
參考:
http://bbs.itheima.com/thread-101106-1-1.html
https://blog.csdn.net/lcfeng1982/article/details/6830584