最近面試,碰到的面試題。面試
Q:中文字符在UTF-8佔幾個字節?UTF-8是固定字長編碼仍是動態字長編碼的?windows
A:UTF-8編碼下一個中文所佔字節也是不肯定的。多是2個、3個、4個字節。所以可見UTF-8是變長的編碼格式的。網絡
先從字符編碼講起編碼
一、美國人首先對其英文字符進行了編碼,也就是最先的ASCII碼,用一個字節的低7位來表示英文的128個字符,高1位統一爲0spa
二、後來歐洲人發現尼瑪你這128位哪夠用,好比我高貴的法國人字母上面的還有註音符,這個怎麼區分?得,把高1位編進來吧,這樣歐洲廣泛使用一個全字節進行編碼,最多可表示256位。歐美人就是喜歡直來直去,字符少,編碼用得位數少翻譯
三、可是即便位數少,不一樣國家地區用不一樣的字符編碼,雖然0--127表示的符號是同樣的。可是128--255這一段的解釋徹底亂套了,即便2進制徹底同樣,表示的字符徹底不同。好比135在法語,希伯來語,俄語編碼中徹底是不一樣的符號。更麻煩的是,尼瑪這電腦高科技傳到中國後,中國人發現咱們有10萬多個漢字,大家歐美這256字塞牙縫都不夠。因而就發明了GB2312這些漢字編碼,典型的用2個字節來表示絕大部分的經常使用漢字,最多能夠表示65536個漢字字符,這樣就不難理解有些漢字你在新華字典裏查獲得,可是電腦上若是不處理一下你是顯示不出來的了吧設計
四、這下各用各的字符集編碼,這世界咋統一?
俄國人發封Email給中國人,兩邊字符集編碼不一樣,尼瑪顯示都是亂碼啊。爲了統一,因而就發明了Unicode,將世界上全部的符號都歸入其中,每個符號都給予一個獨一無二的編碼,如今Unicode能夠容納100多萬個符號,每一個符號的編碼都不同,這下可統一了,全部語言均可以互通,一個網頁頁面裏能夠同時顯示各國文字code
五、然而,Unicode雖然統一了全世界字符的二進制編碼,但沒有規定如何存儲啊,親。x86和amd體系結構的電腦小端序和大端序都分不清,別提計算機如何識別究竟是Unicode仍是ASCII了。
若是Unicode統一規定,每一個符號用三個或四個字節表示,那麼每一個英文字母前都必然有二到三個字節是0,文本文件的大小會所以大出二三倍,這對於存儲來講是極大的浪費。
這樣致使一個後果:出現了Unicode的多種存儲方式orm
六、互聯網的興起,網頁上要顯示各類字符,必須統一啊,親。UTF-8就是Unicode最重要的實現方式之一。另外還有UTF-1六、UTF-32等。UTF-8不是固定字長編碼的,而是一種變長的編碼方式。它可使用1~4個字節表示一個符號,根據不一樣的符號而變化字節長度。這是種比較巧妙的設計,若是一個字節的第一位是0,則這個字節單獨就是一個字符;若是第一位是1,則連續有多少個1,就表示當前字符佔用多少個字節blog
七、注意Unicode的字符編碼和UTF-8的存儲編碼表示是不一樣的,例如"嚴"字的Unicode碼是4E25,UTF-8編碼是E4B8A5,這個7裏面解釋了的,UTF-8編碼不只考慮了編碼,還考慮了存儲,E4B8A5是在存儲識別編碼的基礎上塞進了4E25
八、UTF-8 使用一至四個字節爲每一個字符編碼。128 個 ASCII 字符(Unicode 範圍由 U+0000 至 U+007F)只需一個字節,帶有變音符號的拉丁文、希臘文、西裏爾字母、亞美尼亞語、希伯來文、阿拉伯文、敘利亞文及馬爾代夫語(Unicode 範圍由 U+0080 至 U+07FF)須要二個字節,其餘基本多文種平面(BMP)中的字符(CJK屬於此類-Qieqie注)使用三個字節,其餘 Unicode 輔助平面的字符使用四字節編碼。
九、最後,常規來看,中文漢字在UTF-8中到底佔幾個字節,通常是3個字節,最多見的編碼方式是1110xxxx 10xxxxxx
學過計算機的人都知道 ASCII 碼,總共有 128 個,用一個字節的低 7 位表示,0~31 是控制字符如換行回車刪除等;32~126 是打印字符,能夠經過鍵盤輸入而且可以顯示出來。
128 個字符顯然是不夠用的,因而 ISO 組織在 ASCII 碼基礎上又制定了一些列標準用來擴展 ASCII 編碼,它們是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵蓋了大多數西歐語言字符,全部應用的最普遍。ISO-8859-1 仍然是單字節編碼,它總共能表示 256 個字符。
它的全稱是《信息交換用漢字編碼字符集 基本集》,它是雙字節編碼,總的編碼範圍是 A1-F7,其中從 A1-A9 是符號區,總共包含 682 個符號,從 B0-F7 是漢字區,包含 6763 個漢字。
全稱叫《漢字內碼擴展規範》,是國家技術監督局爲 windows95 所制定的新的漢字內碼規範,它的出現是爲了擴展 GB2312,加入更多的漢字,它的編碼範圍是 8140~FEFE(去掉 XX7F)總共有 23940 個碼位,它能表示 21003 個漢字,它的編碼是和 GB2312 兼容的,也就是說用 GB2312 編碼的漢字能夠用 GBK 來解碼,而且不會有亂碼。
全稱是《信息交換用漢字編碼字符集》,是我國的強制標準,它多是單字節、雙字節或者四字節編碼,它的編碼與 GB2312 編碼兼容,這個雖然是國家標準,可是實際應用系統中使用的並不普遍。
說到 UTF 必需要提到 Unicode(Universal Code 統一碼),ISO 試圖想建立一個全新的超語言字典,世界上全部的語言均可以經過這本字典來相互翻譯。可想而知這個字典是多麼的複雜,關於 Unicode 的詳細規範能夠參考相應文檔。Unicode 是 Java 和 XML 的基礎,下面詳細介紹 Unicode 在計算機中的存儲形式。
UTF-16 具體定義了 Unicode 字符在計算機中存取方法。UTF-16 用兩個字節來表示 Unicode 轉化格式,這個是定長的表示方法,不論什麼字符均可以用兩個字節表示,兩個字節是 16 個 bit,因此叫 UTF-16。UTF-16 表示字符很是方便,每兩個字節表示一個字符,這個在字符串操做時就大大簡化了操做,這也是 Java 以 UTF-16 做爲內存的字符存儲格式的一個很重要的緣由。
UTF-16 統一採用兩個字節表示一個字符,雖然在表示上很是簡單方便,可是也有其缺點,有很大一部分字符用一個字節就能夠表示的如今要兩個字節表示,存儲空間放大了一倍,在如今的網絡帶寬還很是有限的今天,這樣會增大網絡傳輸的流量,並且也不必。而 UTF-8 採用了一種變長技術,每一個編碼區域有不一樣的字碼長度。不一樣類型的字符能夠是由 1~4 個字節組成。
UTF-8 有如下編碼規則:
若是一個字節,最高位(第 8 位)爲 0,表示這是一個 ASCII 字符(00 - 7F)。可見,全部 ASCII 編碼已是 UTF-8 了。
若是一個字節,以 11 開頭,連續的 1 的個數暗示這個字符的字節數,例如:110xxxxx 表明它是雙字節 UTF-8 字符的首字節。
若是一個字節,以 10 開始,表示它不是首字節,須要向前查找才能獲得當前字符的首字節
public class CharacterDemo { public static void main(String [] args) { String chinese = "中"; String english = "A"; String cha = "0x20001"; int c = 0x20001; CharacterDemo.characterTest(english); System.out.println("=============以上是英文字符輸出信息================="); CharacterDemo.characterTest(chinese); System.out.println("=============以上是中文字符輸出信息================="); CharacterDemo.characterTest(cha); System.out.println("=========================================="); CharacterDemo.binaryTest(c); System.out.println("=========================================="); } private static void characterTest(String str) { CharacterDemo.getLengthByEncodeing(str,"GB2312"); CharacterDemo.getLengthByEncodeing(str,"GBK"); CharacterDemo.getLengthByEncodeing(str,"GB2312"); CharacterDemo.getLengthByEncodeing(str,"GB18030"); CharacterDemo.getLengthByEncodeing(str,"ISO-8859-1"); CharacterDemo.getLengthByEncodeing(str,"UTF-8"); CharacterDemo.getLengthByEncodeing(str,"UTF-16"); CharacterDemo.getLengthByEncodeing(str,"UTF-16BE"); CharacterDemo.getLengthByEncodeing(str,"UTF-16LE"); } private static void binaryTest(int c) { char[] arr = Character.toChars(c); String s = new String(arr); System.out.println("char array length:" + arr.length); System.out.println("content:| " + s + " |"); System.out.println("String length:" + s.length()); CharacterDemo.getLengthByEncodeing(s, "GB2312"); CharacterDemo.getLengthByEncodeing(s, "GBK"); CharacterDemo.getLengthByEncodeing(s, "GB2312"); CharacterDemo.getLengthByEncodeing(s, "GB18030"); CharacterDemo.getLengthByEncodeing(s, "ISO-8859-1"); CharacterDemo.getLengthByEncodeing(s, "UTF-8"); CharacterDemo.getLengthByEncodeing(s, "UTF-16"); CharacterDemo.getLengthByEncodeing(s, "UTF-16BE"); CharacterDemo.getLengthByEncodeing(s, "UTF-16LE"); } private static void getLengthByEncodeing(String str, String encodingName) { try { System.out.format("編碼格式:%s\t\t", encodingName); System.out.format("字節長度:%s\n",str.getBytes(encodingName).length); }catch (Exception e) { e.printStackTrace(); } } }
編碼格式:GB2312 字節長度:1 編碼格式:GBK 字節長度:1 編碼格式:GB2312 字節長度:1 編碼格式:GB18030 字節長度:1 編碼格式:ISO-8859-1 字節長度:1 編碼格式:UTF-8 字節長度:1 編碼格式:UTF-16 字節長度:4 編碼格式:UTF-16BE 字節長度:2 編碼格式:UTF-16LE 字節長度:2 =============以上是英文字符輸出信息================= 編碼格式:GB2312 字節長度:2 編碼格式:GBK 字節長度:2 編碼格式:GB2312 字節長度:2 編碼格式:GB18030 字節長度:2 編碼格式:ISO-8859-1 字節長度:1 編碼格式:UTF-8 字節長度:3 編碼格式:UTF-16 字節長度:4 編碼格式:UTF-16BE 字節長度:2 編碼格式:UTF-16LE 字節長度:2 =============以上是中文字符輸出信息================= 編碼格式:GB2312 字節長度:7 編碼格式:GBK 字節長度:7 編碼格式:GB2312 字節長度:7 編碼格式:GB18030 字節長度:7 編碼格式:ISO-8859-1 字節長度:7 編碼格式:UTF-8 字節長度:7 編碼格式:UTF-16 字節長度:16 編碼格式:UTF-16BE 字節長度:14 編碼格式:UTF-16LE 字節長度:14 ========================================== char array length:2 content:| 𠀁 | String length:2 編碼格式:GB2312 字節長度:1 編碼格式:GBK 字節長度:1 編碼格式:GB2312 字節長度:1 編碼格式:GB18030 字節長度:4 編碼格式:ISO-8859-1 字節長度:1 編碼格式:UTF-8 字節長度:4 編碼格式:UTF-16 字節長度:6 編碼格式:UTF-16BE 字節長度:4 編碼格式:UTF-16LE 字節長度:4 ==========================================