1. 程式人生 > >“錕斤拷”的前世今生

“錕斤拷”的前世今生

不管是在工作中還是生活中,相信很多同學都被“錕斤拷”深深的毒害過,比如這樣, ![](https://img2020.cnblogs.com/blog/1719198/202009/1719198-20200919113131439-586921687.png) 這樣, ![](https://img2020.cnblogs.com/blog/1719198/202009/1719198-20200919113143845-442556760.png) 還有這樣, ![](https://img2020.cnblogs.com/blog/1719198/202009/1719198-20200919113159220-1907236650.jpg) 那麼究竟是為什麼會出現這些奇怪的字元?接下來我們一探究竟! ### ASCII編碼 在計算機底層都是用`0`和`1`進行儲存的,ASCII編碼將所有的字母及符號進行編碼後轉成二進位制的`0`和`1`進行儲存,字母和符號佔1個位元組(即8bit),標準的ASCII碼規定最高位必須為`0`,因此ASCII編碼只能有128個,轉成十進位制即為0-127。標準的ASCII碼錶如下: ![](https://img2020.cnblogs.com/blog/1719198/202009/1719198-20200919113211246-40948268.jpg) ASCII碼錶只有128個字元,對於英語來說已經夠用了,但是世界上還有很多國家的文字各不相同,這時候就需要一個更加全面的編碼出現。 Unicode(又稱統一碼、萬國碼、單一碼)是電腦科學領域裡的一項業界標準。**它為每種語言中的每個字元設定了統一併且唯一的二進位制編碼**。在表示一個Unicode的字元時,通常會用“U+”然後緊接著一組十六進位制的數字來表示這個字元。 ### UTF-8與GBK `UTF-8`是針對`Unicode`的一種可變長度字元編碼。它可以用來表示`Unicode`標準中的任何字元,而且其編碼中的第一個位元組仍與ASCII相容。UTF-8使用一至四個位元組為每個字元進行編碼。常用的漢字採用`3`個位元組進行編碼。 因為`UTF-8`是針對`Unicode`的一種可變長度的字元編碼,所以它包含了世界上所有字元的編碼,對於那些早錄入的字元,就會優先使用1、2個位元組來儲存,對於遲錄入的字元儲存佔用的位元組就會大一些,這樣,那些遲錄入的字元儲存空間就會很大。 對於一箇中文網站,實際上並不需要其他國家的文字出現,但是中國漢字用`UTF-8`進行編碼,大多數卻佔用了`3`個位元組甚至更多位元組,這樣就造成了不必要的儲存浪費。為了解決這種問題,中華人民共和國全國資訊科技標準化技術委員會制定了一套GB系列的編碼,最常用的就是`GBK`了。 `GBK`編碼英文使用單位元組編碼,完全相容ASCII字元,漢字使用了兩個位元組進行編碼,其編碼範圍從0x8140(表示16進位制)至0xFEFE(剔除xx7F),共23940個碼位,共收錄了21003個漢字,圖形符號 883 個。 為什麼要剔除`xx7F`,因為它對應的ASCII碼錶是`DEL`,意味著要向後刪除一個字元。 ### 為什麼會出現“錕斤拷” Unicode編碼一直持續在收錄各種字元,這就可能會出現各種作業系統支援的Unicode字元不一樣。這也就會導致A上的一個用Unicode編碼的字元,在B上就會出現無法顯示的情況。為了避免這種情況,在Unicode中定義了一個特殊字元�,它的Unicode編碼為0xFFFD。 假如A支援特殊字元`⬆`,但是B並不支援這個`⬆`,那麼在B中將會用�來代替。 ![](https://img2020.cnblogs.com/blog/1719198/202009/1719198-20200919113233413-1652739176.jpg) 這個字元用UTF-8編碼後,十六進位制表示為0xEF 0XBF 0XBD。如果連續出現兩個`⬆`符號,那麼用UTF-8編碼後的十六進位制則表示為0xEF 0XBF 0XBD 0xEF 0XBF 0XBD,這時候再轉碼成GBK,因為GBK中用兩個位元組表示一個字元,那麼上述的字元就成了**錕**(0xEFBF),**斤**(0xBDEF),**拷**(0xBFBD)。出現錕斤拷的原因就是UTF-8轉碼GBK的過程中出現了問題。當然如果想要出現錕斤拷,則至少需要兩個字元出現亂碼。 接下來,我們直接用程式碼來看一下效果: ```java @Test void contextLoads() throws Exception { String str = "�"; String strCode = new String(str.getBytes("UTF-8"), "GBK"); System.out.println(strCode); } ``` 執行結果為`錕�`,前面也說了如果想要出現錕斤拷,則至少需要為兩個字元,現在再修改一下程式碼 ```java @Test void contextLoads() throws Exception { String str = "��"; String strCode = new String(str.getBytes("UTF-8"), "GBK"); System.out.println(strCode); } ``` 執行結果如下為`錕斤拷`。 如果以後再遇到錕斤拷,不要慌,它一定是UTF-8在轉換GBK編碼的時候出現了問題。現在看來GBK編碼雖然減少了記憶體的浪費,但是也帶來了不少問題。 > 參考:zhihu.com/question/23024782 ## 點關注、不迷路 如果覺得文章不錯,歡迎**關注**、**點贊**、**收藏**,你們的支援是我創作的動力,感謝大家。 如果文章寫的有問題,請不要吝嗇,歡迎留言指出,我會及時核查修改。 如果你還想更加深入的瞭解我,可以微信搜尋「**Java旅途**」進行關注。回覆「**1024**」即可獲得學習視訊及精美電子書。每天7:30準時推送技術文章,讓你的上班路不在孤獨,而且每月還有送書活動,助你提升硬