【原創】通俗易懂地解決中文亂碼問題(1) --- 跨平台亂碼


本來是只打算寫一篇關於中文亂碼的blog的,但是發現要講的東西跨度有點大,不好寫到同一篇里面,所以分開了。

另一篇是  【原創】通俗易懂地解決中文亂碼問題(2) --- 分析解決Mysql插入移動端表情符報錯 ‘incorrect string value: '\xF0...

這一篇重點在編碼的理論,另一篇重點在解決問題及思路。

 

一、問題的開始

中文亂碼問題經常出現在實際工程中,尤其容易發生在經驗不足的團隊對問題預估不足的情況下。網站開發,社交聊天等對輸入信息不可控的應用往往是重災區。再加上移動互聯網的火熱,新興字符和表情也開始頻繁被使用,如果不能達到足夠的支持,對用戶體驗來說是個災難。所以,在設計系統的開始,要嚴格把控字符編碼。以大體明確怎么做是不會出錯的(嚴格按規則限制往往難以把控,並且容易漏掉被鑽空子,所以做到不出錯是出發點)。

 

二、Unicode

Unicode是本文的重點。

Unicode是一種通用字符集,是對字符的定義。和之對應的也有,比如 ISO 8859-1。但Unicode被廣泛使用並成為業界的標准,所以我們可以認為Unicode就是對計算機里字符的定義,在內存中的表現是0,1串。

而且,Unicode的編碼很干凈,它為 字符而非字形定義唯一的代碼。換句話說,統一碼以一種抽象的方式(即數字)來處理字符,並將視覺上的演繹工作(例如字體大小、外觀形狀、字體形態、文體等)留給其他軟件來處理,例如網頁瀏覽器或是文字處理器。舉個例子,如 “ɑ/a”、“強/強”、“戶/戶/戸”。(引自wiki)

這樣,Unicode本身要做的任務很明確,就是合適的擴充編碼。

 

三、UTF-8

UTF-8是Unicode的一種實現方式,是可變長的字符編碼。與之對應的還有GBK(固定長度),Latin,UTF-16(Unicode的完全式)等等。

這些為什么是實現方式呢?不是Unicode已經定義好了嗎?其實這也是計算機學科經常用的方法,這些不同的編碼就類似於針對Unicode的各種trick。

舉個簡單的例子,一個整形數組A[],那么給A[]排序這個定義就相當於Unicode。那么是采用快排、堆排還是歸並,用正序還是倒序排列結果,這些方法就相當於編碼格式這些都是對這個定義的具體實現過程,但是方法不同。

因此,不論是UTF-8、GBK、Latin等,其還原的編碼結果都是同一個Unicode編碼。

 

四、Unicode和UTF-8的關系及轉換過程

那么對於Unicode和UTF-8的關系,可以用上面的例子理解。不過真實情況應該是類似下面這樣的。

比如"國"字的Unicode編碼定義為 00000000 00000000 00110100 11000000 (假設)。

由於其低16位都是0,為了減少存儲和傳輸這個字在字節上的浪費,就選擇高16位來表示。同時由於UTF-8是可變長的,所以需要標識位來標識這個編碼到底使用了幾個字節。

所以 “國”字 對應的 UTF-8的編碼應該是 11100011 10010011 10000000(加粗的是原編碼的高16位)。

轉換公式:1110xxxx(E0-EF) 10yyyyyy 10zzzzzz,顯示標明的是標志位)。

以下附上UTF-8的編碼方式:(引自wiki)

 

 

 

五、中文跨平台亂碼及解決辦法

有了以上知識的積累,我們可以分析為什么跨平台會出現亂碼?明明好好的Unicode怎么就亂了呢?

那么很直觀我們會想到應該是編碼格式不兼容

對於windows平台,編碼格式是GBK,對應的漢字是兩個字節長度。對於Linux平台,編碼格式是UTF-8,對應的漢字是3個字節。(這里都是默認情況)

那么我們還用上面 排序這個例子來解釋。

比如現在Unicode用 {1,2,3}定義,GBK代表正序排列,UTF-8代表倒序排列。那么現在Unicode在GBK下的編碼是{1,2,3},在UTF-8下的編碼是{3,2,1}。

現在由GBK編碼還原Unicode編碼,那么正向解析GBK就是Unicode。而由UTF-8編碼還原Unicode編碼,需要逆向解析UTF-8編碼。這都是和自身對應的。

但是如果一個把GBK編碼誤認為是UTF-8,那么逆向解析后的結果是{3,2,1}。首先這個結果不是原始的Unicode編碼,那么其轉換的結果不是我們需要的。其次,很可能這個編碼結果在Unicode中還沒有定義,可能會出現類似空格一樣的空白符。

因此這就是跨平台中文亂碼的原因,編碼和解碼方式出現了差別。

 

解決辦法:

解決辦法其實有很多種,根據自身應用的不同既可以選擇在代碼端進行編碼的轉換(比如java的 String str = new String(str.getBytes("GBK"), "UTF-8");),也可以在輸入端進行編碼格式的調整。

不過歸結下來只有一點,即 如果當前輸入是GBK編碼,而你需要的又是UTF-8編碼,那么:

1. 用GBK的解碼方式轉換成Unicode。

2. 使用UTF-8編碼進行編碼。

 

六、中文編碼一些有趣的應用

這里我只想到了war3(感謝@僵屍的提醒)上面一些搞笑的名字(一不小心貌似暴露了什么。。),后面想到其它還會更新的。

比如下面這個圖:

藍圈里面的玩家名字是正常顯示的,為綠色。紅圈里面的是藍色,相當於突破了war3的限制顯示了其它特殊顏色。

這個做法就是對應名字后面加上|r。這是一個轉義符,合理的利用了war3給用戶開放的字符集並產生了特殊效果。所以這也是我開篇說的嚴格把控字符集是很難的,控制到不出錯(比如系統亂碼)已經挺好了。

 

轉載請注明出處,謝謝~  http://www.cnblogs.com/xiaoboCSer/p/4175361.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM