天天看點

“锟斤拷“的前世今生

不管是在工作中還是生活中,相信很多同學都被“锟斤拷”深深的毒害過,比如這樣,

“锟斤拷“的前世今生
這樣,
“锟斤拷“的前世今生
還有這樣,
“锟斤拷“的前世今生
那麼究竟是為什麼會出現這些奇怪的字元?接下來我們一探究竟!

ASCII編碼

在計算機底層都是用

1

進行存儲的,ASCII編碼将所有的字母及符号進行編碼後轉成二進制的

1

進行存儲,字母和符号占1個位元組(即8bit),标準的ASCII碼規定最高位必須為

,是以ASCII編碼隻能有128個,轉成十進制即為0-127。标準的ASCII碼表如下:

“锟斤拷“的前世今生

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中将會用�來代替。

“锟斤拷“的前世今生

這個字元用UTF-8編碼後,十六進制表示為0xEF 0XBF 0XBD。如果連續出現兩個

符号,那麼用UTF-8編碼後的十六進制則表示為0xEF 0XBF 0XBD 0xEF 0XBF 0XBD,這時候再轉碼成GBK,因為GBK中用兩個位元組表示一個字元,那麼上述的字元就成了锟(0xEFBF),斤(0xBDEF),拷(0xBFBD)。出現锟斤拷的原因就是UTF-8轉碼GBK的過程中出現了問題。當然如果想要出現锟斤拷,則至少需要兩個字元出現亂碼。

接下來,我們直接用代碼來看一下效果:

@Test
void contextLoads() throws Exception {
    String str = "�";
    String strCode = new String(str.getBytes("UTF-8"), "GBK");
    System.out.println(strCode);
}           

運作結果為

锟�

,前面也說了如果想要出現锟斤拷,則至少需要為兩個字元,現在再修改一下代碼

@Test
void contextLoads() throws Exception {
    String str = "��";
    String strCode = new String(str.getBytes("UTF-8"), "GBK");
    System.out.println(strCode);
}           

運作結果如下為

锟斤拷

如果以後再遇到锟斤拷,不要慌,它一定是UTF-8在轉換GBK編碼的時候出現了問題。現在看來GBK編碼雖然減少了記憶體的浪費,但是也帶來了不少問題。