首頁 >Java >java教程 >java中文亂碼解決之道

java中文亂碼解決之道

伊谢尔伦
伊谢尔伦原創
2016-11-26 09:55:391512瀏覽

隨著電腦的發展、普及,世界各國為了適應本國的語言和字符都會自己設計一套自己的編碼風格,正是由於這種亂,導致存在很多種編碼方式,以至於同一個二進制數字可能會被解釋成不同的符號。為了解決這種不相容的問題,偉大的創想Unicode編碼應時而生! !

Unicode

Unicode又稱為統一碼、萬國碼、單碼,它是為了解決傳統的字元編碼方案的局限而產生的,它為每種語言中的每個字元設定了統一並且唯一的二進位編碼,以滿足跨語言、跨平台進行文字轉換、處理的要求。可以想像Unicode作為一個“字符大容器”,它將世界上所有的符號都包含其中,並且每一個符號都有自己獨一無二的編碼,這樣就從根本上解決了亂碼的問題。所以Unicode是一種所有符號的編碼[2]。

Unicode伴隨著通用字元集的標準而發展,同時也以書本的形式對外發表,它是業界的標準,對世界上大部分的文字系統進行了整理、編碼,使得電腦可以用更為簡單的方式來呈現和處理文字。 Unicode至今仍在不斷增修,迄今而至已收入超過十萬個字符,它備受業界認可,並廣泛地應用於電腦軟體的國際化與本地化過程。

我們知道Unicode是為了解決傳統的字元編碼方案的限製而產生的,對於傳統的編碼方式而言,他們都存在一個共同的問題:無法支援多語言環境,這對於互聯網這個開放的環境是不允許的。而目前幾乎所有的電腦系統都支援基本拉丁字母,並各自支援不同的其他編碼方式。 Unicode為了和它們相互相容,其首256字符保留給ISO 8859-1所定義的字符,使既有的西歐語系文字的轉換不需特別考慮;並且把大量相同的字符重複編到不同的字符碼中去,使得舊有紛雜的編碼方式得以和Unicode編碼間互相直接轉換,而不會丟失任何資訊[1]。

實作方式

一個字元的Unicode編碼是確定的,但是在實際傳輸過程中,由於不同系統平台的設計不一定一致,以及出於節省空間的目的,對Unicode編碼的實現方式有所不同。 Unicode的實作方式稱為Unicode轉換格式(Unicode Transformation Format,簡稱UTF)[1]。

Unicode是字元集,它主要有UTF-8、UTF-16、UTF-32三種實作方式。由於UTF-8是目前主流的實現方式,UTF-16、UTF-32相對而言使用較少,所以以下就主要介紹UTF-8。

UCS

提到Unicode可能有必要了解下,UCS。 UCS(Universal Character Set,通用字元集),是由ISO制定的ISO 10646(或稱為ISO/IEC 10646)標準所定義的標準字元集。它包括了其他所有字元集,保證了與其他字元集的雙向相容,即,如果你將任何文字字串翻譯到UCS格式,然後再翻譯回原始編碼,你不會丟失任何資訊。

UCS不僅為每個字元分配一個代碼,而且還賦予了一個正式的名字。表示一個UCS或Unicode值的十六進位數通常在前面加上“U+”,例如“U+0041”代表字元“A”。

Little endian & Big endian

由於各系統平台的設計不同,可能會導致某些平台對字元的理解不同(例如位元組順序的理解)。這時將會導致同意位元組流可能會被解釋為不同的內容。如某個字元的十六進位為4E59,拆分為4E、59,在MAC上讀取時是歐諾個低位元開始的,那麼MAC在遇到該位元組流時會被解析為594E,找到的字元為“奎”,但是在Windows平台是從高位元組開始讀取,為4E59,找到的字元為“乙”。也就是說在Windows平台保存的「乙」跑到MAC平台上就變成了「奎」。這樣勢必會造成混亂,於是在Unicode編碼中採用了大頭(Big endian)、小頭(Little endian)兩種方式來進行區分。即第一個字節在前,就是大頭方式,第二個字節在前就是小頭方式。那麼這個時候就出現了一個問題:電腦怎麼知道某個檔案到底是採用哪一種編碼方式的呢?

Unicode規範中定義,每個檔案的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做"零寬度非換行空格"(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節,而且FF比FE大1。

如果一個文字檔案的頭兩個位元組是FE FF,就表示該檔案採用大頭方式;如果前兩個位元組是FF FE,就表示該檔案採用小頭方式。

UTF-8

UTF-8是一種針對Unicode的可變長度字元編碼,可以使用1~4個位元組表示一個符號,根據不同的符號而變化位元組長度。它可以用來表示Unicode標準中的任何字符,且其編碼中的第一個字節仍與ASCII相容,這使得原來處理ASCII字符的系統無須或只須做少部修改,即可繼續使用。因此,它逐漸成為電子郵件、網頁及其他儲存或傳送文字的應用中,優先採用的編碼。

UTF-8使用一到四個位元組為每個字元編碼,編碼規則如下:

1)對於單字節的符號,位元組的第一位設為0,後面7位元為這個符號的unicode碼。因此對於英語字母,UTF-8編碼和ASCII碼是相同的。

2)對於n位元組的符號(n>1),第一個位元組的前n位都設為1,第n+1位元設為0,後面位元組的前兩位一律設為10 。剩下的沒有提及的二進位位,全部都是這個符號的unicode碼。

轉換表如下:

java中文亂碼解決之道

根據上面的轉換表,理解UTF-8的轉換編碼規則就變得非常簡單了:第一個位元組的第一位如果為0,則表示這個位元組單獨就是一個字元;如果為1,連續多少個1就表示該字元佔有多少個位元組。

以漢字"嚴"為例,示範如何實現UTF-8編碼[3]。

已知"嚴"的unicode是4E25(100111000100101),根據上表,可以發現4E25處在第三行的範圍內(0000 0800-0000 FFFF),因此"嚴"的UTF-8編碼需要三個字節,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然後,從"嚴"的最後一個二進位位開始,依序從後向前填入格式中的x,多出的位補0。這樣就得到了,"嚴"的UTF-8編碼是"11100100 10111000 10100101",轉換成十六進位就是E4B8A5。

Unicode與UTF-8之間的轉換

透過上面的例子我們可以看到"嚴"的Unicode碼為4E25,UTF-8編碼為E4B8A5,他們兩者是不一樣的,需要透過程式的轉換來實現,在Window平台最簡單的直覺的方法就是記事本。

在最下面的"編碼(E)"處有四個選項:ANSI、Unicode、Unicode big endian、UTF-8。

ANSI:記事本的預設的編碼方式,對於英文檔案是ASCII編碼,對於簡體中文檔案是GB2312編碼。注意:不同ANSI 編碼之間互不相容,當訊息在國際間交流時,無法將屬於兩種語言的文字,儲存在同一段ANSI 編碼的文本中

Unicode:UCS-2編碼方式,即直接使用兩個位元組存入字元的Unicode碼。該方式是"小頭"little endian方式。

Unicode big endian:UCS-2編碼方式,"大頭"方式。

UTF-8:閱讀上面(UTF-8)。

>>>實例:在記事本中輸入"嚴"字,依序選擇ANSI、Unicode、Unicode big endian、UTF-8四種編碼風格,然後另存為,使用EditPlus文字工具使用"16進位檢視器"進行查看,得到以下結果:

ANSI:兩個位元組"D1 CF"正是"嚴"的GB2312編碼。

Unicode:四個位元組"FF FE 25 4E",其中"FF FE"表示小頭儲存方式,真正的編碼為"25 4E"。

Unicode big endian:四個位元組"FE FF 4E 25","FE FF"表示大頭儲存方式,真正編碼為"4E 25"。

UTF-8:編碼是六個位元組"EF BB BF E4 B8 A5",前三個位元組"EF BB BF"表示這是UTF-8編碼,後三個"E4B8A5"就是"嚴"的具體編碼,它的儲存順序與編碼順序是一致的。


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
上一篇:java NIO教程下一篇:java NIO教程