Java中Unicode與utf-八、utf-16的關係

轉載於:https://www.cnblogs.com/fnlingnzb-learner/p/6163205.html

基礎知識:

字符集合(ASCII、Unicode):由一套用於特定用途的字符組成,例如支持西歐語言的字符集合,支持中文的字符集合。字符集合只定義了符號和他們的語意,其實跟計算機沒有直接關係。現實生活中,不一樣的語系有本身的字符集合,例如藏文有本身的字符集合,漢文有本身的字符集合。到計算機的世界中,也有各類字符集合,例如ASCII字符集合,GB2312字符集合,GBK字符集合。還有一個其餘字符集合的超集--Unicode字符集定義了幾乎絕大部分現存語言須要的字符,是一種通用的字符集,來支持多語言環境(能夠同時處理多種語言混合的狀況)。各個國家和地區在制定編碼標準的時候,「字符集合」和「字符編碼」通常都是同時制定的。因此像ASCII字符集合同樣,它也同時表明了一種字符的編碼。html

字符編碼:是一套規則,定義了在計算機內存中如何表示字符,是字符集中的每一個字符與計算機內存中字節之間的轉換關係,也能夠認爲是把字符數字化,規定每一個「字符」分別用一個字節仍是多個字節存儲,用哪些字節來存儲。例如ASCII編碼[你沒看錯,它既是一種字符集合,也是一種字符編碼],定義了英文字母和符號在計算機中的表示方式,是用一個字節來表示。Unicode字符集合,有好幾種字符編碼方式,例如變長度編碼的UTF8,UTF16等。中文字符集也有不少字符編碼,例如上文提到的GB2312編碼,GBK編碼等。java

char、unicode、string和UTF八、UTF16之間的關係描述:Java語言內部使用的就是16位的Unicode編碼,從概念上講java字符串就是Unicode字符序列,Unicode字符集合的碼點(碼點:指與一個編碼表中的某個字符對應的代碼值)能夠分紅17個代碼級別,第一個代碼級別稱爲基本的多語言級別,碼點從U+0000到U+FFFF,即65536個碼點,只有第一級別的碼點能夠用一個char值表示;其他的16個級別碼點從U+10000到U+10FFFF,其中包含一些輔助字符,須要兩個char值才能表示一個碼點;Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲,二進制與16進制等能夠靈活轉換,只是數值的表示方式變化而已,其值的大小不變。網絡

UTF八、UTF16即爲採用什麼樣的規則表示全部的碼點,在UTF16的編碼規則中,UTF16採用不一樣長度的編碼表示全部的Unicode碼點,在基本的多語言級別,每一個字符用16位表示,一般被稱爲代碼單元;而輔助字符采用一對連續的代碼單元進行編碼。U+D800~U+DFFF爲空閒的2048個替代區域,U+D800~U+DBFF用於第一個代碼單元,U+DC00~U+DFFF用於第二個代碼單元,這樣的設計能夠迅速的知道一個代碼單元是一個字符的編碼仍是一個輔助字符的第一或第二部分。

性能

爲啥須要Unicode

 

      咱們知道計算機其實挺笨的,它只認識0101這樣的字符串,固然了咱們看這樣的01串時確定會比較頭暈的,因此不少時候爲了描述簡單都用十進制,十六進制,八進制表示.實際上都是等價的,沒啥太多不同.其餘啥文字圖片之類的其餘東東計算機不認識.那爲了在計算機上表示這些信息就必須轉換成一些數字.你確定不能想怎麼轉換就怎麼轉,必須得有定些規則.因而剛開始的時候就有ASCII字符集(American Standard Code for Information Interchange, "美國信息交換標準碼),它使用7 bits來表示一個字符,總共表示128個字符,咱們通常都是用字節(byte,即8個01串)來做爲基本單位.那麼怎麼當用一個字節來表示字符時第一個bit老是0,剩下的七個字節就來表示實際內容.後來IBM公司在此基礎上進行了擴展,用8bit來表示一個字符,總共能夠表示256個字符.也就是當第一個bit是0時仍表示以前那些經常使用的字符.當爲1時就表示其餘補充的字符.編碼

        英文字母再加一些其餘標點字符之類的也不會超過256個.一個字節表示主足夠了.但其餘一些文字不止這麼多 ,像漢字就上萬個.因而又出現了其餘各類字符集.這樣不一樣的字符集交換數據時就有問題了.可能你用某個數字表示字符A,但另外的字符集又是用另一個數字表示A.這樣交互起來就麻煩了.因而就出現了Unicode和ISO這樣的組織來統一制定一個標準,任何一個字符只對應一個肯定的數字.ISO取的名字叫UCS(Universal Character Set),Unicode取的名字就叫unicode了.spa

      總結起來爲啥須要Unicode就是爲了適應全球化的發展,便於不一樣語言之間的兼容交互,而ASCII再也不能勝任此任務了.操作系統

 

Unicode詳細介紹

 

1.容易產生後歧義的兩字節設計

        unicode的第一個版本是用兩個字節(16bit)來表示全部字符code

        .實際上這麼說容易讓人產生歧義,咱們總以爲兩個字節就表明保存在計算機中時是兩個字節.因而任何字符若是用unicode表示的話保存下來都佔兩個字節.其實這種說法是錯誤的.orm

     其實Unicode涉及到兩個步驟,首先是定義一個規範,給全部的字符指定一個惟一對應的數字,這徹底是數學問題,能夠跟計算機沒半毛錢關係.第二步纔是怎麼把字符對應的數字保存在計算機中,這才涉及到實際在計算機中佔多少字節空間.

     因此咱們也能夠這樣理解,Unicode是用0至65535之間的數字來表示全部字符.其中0至127這128個數字表示的字符仍然跟ASCII徹底同樣.65536是2的16次方.這是第一步.第二步就是怎麼把0至65535這些數字轉化成01串保存到計算機中.這確定就有不一樣的保存方式了.因而出現了UTF(unicode transformation format),有UTF-8,UTF-16.

 

2.UTF-8 與UTF-16的區別

    UTF-16比較好理解,就是任何字符對應的數字都用兩個字節來保存.咱們一般對Unicode的誤解就是把Unicode與UTF-16等同了.可是很顯然若是都是英文字母這作有點浪費.明明用一個字節能表示一個字符爲啥整兩個啊.

   因而又有個UTF-8,這裏的8很是容易誤導人,8不是指一個字節,難道一個字節表示一個字符?實際上不是.當用UTF-8時表示一個字符是可變的,有多是用一個字節表示一個字符,也多是兩個,三個..反正是根據字符對應的數字大小來肯定.

   因而UTF-8和UTF-16的優劣很容易就看出來了.若是所有英文或英文與其餘文字混合,但英文佔絕大部分,用UTF-8就比UTF-16節省了不少空間.而若是所有是中文這樣相似的字符或者混合字符中中文佔絕大多數.UTF-16就佔優點了,能夠節省不少空間.另外還有個容錯問題,等會再講

 

  看的有點暈了吧,舉個例子.假如中文字"漢"對應的unicode是6C49(這是用十六進制表示,用十進制表示是27721爲啥不用十進制表示呢?很明顯用十六進制表示要短點.其實都是等價的沒啥不同.就跟你說60分鐘和1小時同樣.).你可能會問當用程序打開一個文件時咱們怎麼知道那是用的UTF-8仍是UTF-16啊.天然會有點啥標誌,在文件的開頭幾個字節就是標誌.

EF BB BF 表示UTF-8

FE FF 表示UTF-16.

 

用UTF-16表示"漢"

假如用UTF-16表示的話就是01101100   01001001(共16 bit,兩個字節).程序解析的時候知道是UTF-16就把兩個字節當成一個單元來解析.這個很簡單.

用UTF-8表示"漢"

用UTF-8就有複雜點.由於此時程序是把一個字節一個字節的來讀取,而後再根據字節中開頭的bit標誌來識別是該把1個仍是兩個或三個字節作爲一個單元來處理.

0xxxxxxx,若是是這樣的01串,也就是以0開頭後面是啥就不用管了XX表明任意bit.就表示把一個字節作爲一個單元.就跟ASCII徹底同樣.

110xxxxx 10xxxxxx.若是是這樣的格式,則把兩個字節當一個單元

1110xxxx 10xxxxxx 10xxxxxx 若是是這種格式則是三個字節當一個單元.

這是約定的規則.你用UTF-8來表示時必須遵照這樣的規則.咱們知道UTF-16不須要用啥字符來作標誌,因此兩字節也就是2的16次能表示65536個字符.

而UTF-8因爲裏面有額外的標誌信息,全部一個字節只能表示2的7次方128個字符,兩個字節只能表示2的11次方2048個字符.而三個字節能表示2的16次方,65536個字符.

因爲"漢"的編碼27721大於2048了全部兩個字節還不夠,只能用三個字節來表示.

全部要用1110xxxx 10xxxxxx 10xxxxxx這種格式.把27721對應的二進制從左到右填充XXX符號(實際上不必定從左到右,也能夠從右到左,這是涉及到另一個問題.等會說.

剛說到填充方式能夠不同,因而就出現了Big-Endian,Little-Endian的術語.Big-Endian就是從左到右,Little-Endian是從右到左.

由上面咱們能夠看出UTF-8在局部的字節錯誤(丟失、增長、改變)不會致使連鎖性的錯誤,由於 UTF-8 的字符邊界很容易檢測出來,因此容錯性較高。

 

Unicode版本2

    前面說的都是unicode的第一個版本.但65536顯然不算太多的數字,用它來表示經常使用的字符是沒一點問題.足夠了,但若是加上不少特殊的就也不夠了.因而從1996年開始又來了第二個版本.用四個字節表示全部字符.這樣就出現了UTF-8,UTF16,UTF-32.原理和以前確定是徹底同樣的,UTF-32就是把全部的字符都用32bit也就是4個字節來表示.而後UTF-8,UTF-16就視狀況而定了.UTF-8能夠選擇1至8個字節中的任一個來表示.而UTF-16只能是選兩字節或四字節..因爲unicode版本2的原理徹底是同樣的,就很少說了.

前面說了要知道具體是哪一種編碼方式,須要判斷文本開頭的標誌,下面是全部編碼對應的開頭標誌

EF BB BF    UTF-8
FE FF     UTF-16/UCS-2, little endian
FF FE     UTF-16/UCS-2, big endian
FF FE 00 00  UTF-32/UCS-4, little endian.
00 00 FE FF  UTF-32/UCS-4, big-endian.

其中的UCS就是前面說的ISO制定的標準,和Unicode是徹底同樣的,只不過名字不同.ucs-2對應utf-16,ucs-4對應UTF-32.UTF-8是沒有對應的UCS


UTF-16 並非一個完美的選擇,它存在幾個方面的問題:
  1. UTF-16 能表示的字符數有 6 萬多,看起來不少,可是實際上目前 Unicode 5.0 收錄的字符已經達到 99024 個字符,早已超過 UTF-16 的存儲範圍;這直接致使 UTF-16 地位頗爲尷尬——若是誰還在想着只要使用 UTF-16 就能夠高枕無憂的話,恐怕要失望了
  2. UTF-16 存在大小端字節序問題,這個問題在進行信息交換時特別突出——若是字節序未協商好,將致使亂碼;若是協商好,可是雙方一個採用大端一個採用小端,則必然有一方要進行大小端轉換,性能損失不可避免(大小端問題其實不像看起來那麼簡單,有時會涉及硬件、操做系統、上層軟件多個層次,可能會進行屢次轉換)
  3. 另外,容錯性低有時候也是一大問題——局部的字節錯誤,特別是丟失或增長可能致使全部後續字符所有錯亂,錯亂後要想恢復,可能很簡單,也可能會很是困難。(這一點在平常生活裏你們感受彷佛可有可無,可是在不少特殊環境下倒是巨大的缺陷)
目前支撐咱們繼續使用 UTF-16 的理由主要是考慮到它是雙字節的,在計算字符串長度、執行索引操做時速度很快。固然這些優勢 UTF-32 都具備,但不少人畢竟仍是以爲 UTF-32 太佔空間了。

反過來 UTF-8 也不完美,也存在一些問題:
  1. 文化上的不平衡——對於歐美地區一些以英語爲母語的國家 UTF-8 簡直是太棒了,由於它和 ASCII 同樣,一個字符只佔一個字節,沒有任何額外的存儲負擔;可是對於中日韓等國家來講,UTF-8 實在是太冗餘,一個字符居然要佔用 3 個字節,存儲和傳輸的效率不但沒有提高,反而降低了。因此歐美人民經常絕不猶豫的採用 UTF-8,而咱們卻總是要猶豫一下子
  2. 變長字節表示帶來的效率問題——你們對 UTF-8 疑慮重重的一個問題就是在於其由於是變長字節表示,所以不管是計算字符數,仍是執行索引操做效率都不高。爲了解決這個問題,經常會考慮把 UTF-8 先轉換爲 UTF-16 或者 UTF-32 後再操做,操做完畢後再轉換回去。而這顯然是一種性能負擔。


固然,UTF-8 的優勢也不能忘了:
  1. 字符空間足夠大,將來 Unicode 新標準收錄更多字符,UTF-8 也能妥妥的兼容,所以不會再出現 UTF-16 那樣的尷尬
  2. 不存在大小端字節序問題,信息交換時很是便捷
  3. 容錯性高,局部的字節錯誤(丟失、增長、改變)不會致使連鎖性的錯誤,由於 UTF-8 的字符邊界很容易檢測出來,這是一個巨大的優勢(正是爲了實現這一點,我們中日韓人民不得不忍受 3 字節 1 個字符的苦日子)

那麼到底該如何選擇呢?

由於不管是 UTF-8 和 UTF-16/32 都各有優缺點,所以 選擇的時候應當立足於實際的應用場景。例如在個人習慣中,存儲在磁盤上或進行網絡交換時都會採用 UTF-8,而在程序內部進行處理時則轉換爲 UTF-16/32。對於大多數簡單的程序來講,這樣作既能夠保證信息交換時容易實現相互兼容,同時在內部處理時會比較簡單,性能也還算不錯。(基本上只要你的程序不是 I/O 密集型的均可以這麼幹,固然這只是我粗淺的認識範圍內的經驗,極可能會被無情的反駁)

稍微再展開那麼一點點…… 在一些特殊的領域,字符編碼的選擇會成爲一個很關鍵的問題。特別是一些高性能網絡處理程序裏更是如此。這時採用一些特殊的設計技巧,能夠緩解性能和字符集選擇之間的矛盾。例如對於內容檢測/過濾系統,須要面對任何可能的字符編碼,這時若是還採用把各類不一樣的編碼都轉換爲同一種編碼後再處理的方案,那麼性能降低將會很顯著。而若是採用多字符編碼支持的有限狀態機方案,則既可以無需轉換編碼,同時又可以以極高的性能進行處理。固然如何從規則列表生成有限狀態機,如何使得有限狀態機支持多編碼,以及這將帶來哪些限制,已經又成了另外的問題了。
相關文章
相關標籤/搜索