字符編碼的理解

今天遇到了個字符編碼的問題,在這總結一下本身的簡單理解:java

1.首先區分字符集和字符編碼這兩個概念才比較好理解數組

字符集:就是一個文字,標點符號等的集合編碼

字符編碼:就是把字符集中的字符和其餘指定集合的一個元素相互對應。例如:unicode編碼把unicode字符集和0x00000000~0xFFFFFFFF對應,ascii編碼把ascii字符集和0~127.操作系統

2.ascii,iso-8859-1,GB,unicode等字符集code

計算機出現不久後,爲了計算機的發展,美國發布了ascii標準,這個字符集只有128個,對應這7個二進制。因爲美國計算機一直領先,於是這個標準影響普遍。可是對於歐洲其餘一些國家而言例如:法國,德國,ascii字符集並無包含法語,德語全部的字符。於是在ascii字符集基礎上擴展了iso-8859-1,iso-8859-1字符集有256個字符,用一個字節表示完,iso-8859-1前128個字符和ascii相同內存

對於中國而言,ascii只是英文,並不適合中國平時處理漢字的須要,於是中國在ascii字符集基礎上擴展增長了漢子造成gb2312字符集,他用兩個字節表示漢字,GB 2312的出現,基本知足了漢字的計算機處理須要,但對於人名、古漢語等方面出現的罕用字,GB 2312不能處理,這致使了後來GBK及GB 18030的出現。utf-8

unicode:各國國家都有本身的字符集和字符編碼方案,爲了統一世界全部的字符出現了unicode,使用0x00000000~0xFFFFFFFF來表示,共4個字節。可是實際目前只是用了0x00000000~0x0010FFFF共100多萬個。這100多萬分布在 17 個 代碼平面(code plane,每一個平面有2的16次方個)裏面。ci

U+0000 ~ U+FFFF       基本多語言平面BMP(Basic Multilingual Plane),unicode

U+10000 ~ U+10FFFF  輔助平面SMP (Supplementary Plane), 這些處於輔助平面的字符咱們稱做 增補字符(supplementary characters)。字符串

3   unicode編碼方案

Unicode只是一個符號集,unicode編碼它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。所以要有編碼方案把unicode字符映射到能夠適合存儲的二進制。經常使用的方案有:utf-32,utf-16,utf-8

utf-32:用4個字節表示一個unicode的字符,例如:a用0x00000061。但這種效率很低,由於unicode自己只用了100多萬,而4字節(2的32次方)有40多億。

utf-16: 用2個字節或4個字節表示一個unicode,例如:a用0x0061。對於基本多語言平面BMP(Basic Multilingual Plane)用兩個字節表示,對於輔助平面SMP (Supplementary Plane)用四個字節表示,即一個輔助平面的字符,被拆成兩個基本平面的字符表示,其原理:在基本平面內,從U+D800到U+DFFF是一個空段,即這些碼點不對應任何字符。所以,這個空段能夠用來映射輔助平面的字符。

U+D800到U+DBFF(空間大小210),稱爲高位(H),

U+DC00到U+DFFF(空間大小210),稱爲低位(L)。

因此,當咱們遇到兩個字節,發現它的碼點在U+D800到U+DBFF之間,就能夠判定,緊跟在後面的兩個字節的碼點,應該在U+DC00到U+DFFF之間,這四個字節必須放在一塊兒解讀。

Unicode碼點轉成UTF-16的時候,首先區分這是基本平面字符,仍是輔助平面字符。若是是前者,直接將碼點轉爲對應的十六進制形式,長度爲兩字節。例如:‘張’   這個字符 unicode編碼位 U+5F 20,其utf16爲0x5F 20。 若是是輔助平面字符,使用轉碼公式:

H = Math.floor((c-0x10000) / 0x400)+0xD800
L = (c - 0x10000) % 0x400 + 0xDC00

utf-8:是一種更加節省空間的編碼方案,它是一種變長的編碼方法,字符長度從1個字節到4個字節不等。越是經常使用的字符,字節越短,最前面的128個字符,只使用1個字節表示,與ASCII碼徹底相同。

編號範圍                                 字節
0x0000 - 0x007F        1
0x0080 - 0x07FF        2
0x0800 - 0xFFFF        3
0x010000 - 0x10FFFF      4

4.java中char和字符串的字符編碼問題

 Java採用UTF-16編碼做爲內碼,也就是說在JVM內部文本字符的編碼爲UTF-16,java經常使用的文本類型就是字符(char)和字符串(String),可是在 Java 中 char 數據類型是定長的(換種說法就是char類型的編碼是一種帶有限制的UTF-16編碼,由於UTF-16的一個字符的長度多是2字節也多是4字節),其長度永遠只有 16 位,char 數據類型永遠只能表示代碼點在 U+0000 ~ U+FFFF 之間的字符,也就是在 BMP 內的字符,若是超過了這個範圍,即便用了增補字符,那麼 char 數據類型將沒法支持。String也是UTF-16,可是string中的字符沒有永遠只有 16 位這個限制,它可以支持增補字符,即它的一個字符的長度多是16位也多是32位。

其中要注意字符串長度問題:

對於string,其底層是char[]  即char數組,其length()方法是數組長度,可是若是是想知道字符串中完整字符的長度要用codePointCount方法。

解釋:一個完整的「字符」是一個code point;一個code point能夠對應1到2個code unit;一個code unit是16位。只有只需1個code unit的code point才能夠完整的存在char裏。但String做爲char的序列,能夠包含由兩個code unit組成的「surrogate pair」來表示須要2個code unit表示的UTF-16 code point。爲此Java的標準庫新加了一套用於訪問code point的API,而這套API就表現出了UTF-16的變長特性。

在這總結一下在java中從源碼到運行要碰到的字符編碼問題,字符一般會出如今三個地方:

  1. Java源碼文件,*.java,能夠是任意字符編碼,如GBK,UTF-8
  2. Class文件,*.class,採用的是一種改進的UTF-8編碼
  3.  JVM,內存中使用UTF-16編碼

首先從源碼開始,源碼java文件能夠GBK,UTF-8等編寫。寫好源碼後要編譯,這時Java編譯器須要知道源碼文件的編碼才能正確編譯,默認狀況下編譯器它會取操做系統的編碼,可使用參數-encoding指定源碼文件的字符編碼。編譯器正確讀取源碼後會編譯成UTF-8編碼的Class文件。運行時,JVM加載Class文件,把其中的字符或字符串轉成UTF-16編碼序列