編碼的本質(zhì)和亂碼的恢復(fù) (下)

亂碼

上節(jié)說到亂碼出現(xiàn)的主要原因,即在進(jìn)行編碼轉(zhuǎn)換的時(shí)候,如果將原來的編碼識(shí)別錯(cuò)了,并進(jìn)行了轉(zhuǎn)換,就會(huì)發(fā)生亂碼,而且這時(shí)候無論怎么切換查看編碼的方式,都是不行的。

我們來看一個(gè)這種錯(cuò)誤轉(zhuǎn)換后的亂碼,還是用上節(jié)的例子,二進(jìn)制是(16進(jìn)制表示):C3 80 C3 8F C3 82 C3 AD,無論按哪種編碼解析看上去都是亂碼:

UTF-8 à??í
Windows-1252 ?€?????-
GB18030 脌脧脗鉚
Big5 ???穩(wěn)

雖然有這么多形式,但我們看到的亂碼形式很可能是"à??í",因?yàn)樵诶又蠻TF-8是編碼轉(zhuǎn)換的目標(biāo)編碼格式,既然轉(zhuǎn)換為了UTF-8,一般也是要按UTF-8查看。

亂碼恢復(fù)

"亂"主要是因?yàn)榘l(fā)生了一次錯(cuò)誤的編碼轉(zhuǎn)換,恢復(fù)是要恢復(fù)兩個(gè)關(guān)鍵信息,一個(gè)是原來的二進(jìn)制編碼方式A,另一個(gè)是錯(cuò)誤解讀的編碼方式B。

恢復(fù)的基本思路是嘗試進(jìn)行逆向操作,假定按一種編碼轉(zhuǎn)換方式B獲取亂碼的二進(jìn)制格式,然后再假定一種編碼解讀方式A解讀這個(gè)二進(jìn)制,查看其看上去的形式,這個(gè)要嘗試多種編碼,如果能找到看著正常的字符形式,那應(yīng)該就可以恢復(fù)。

這個(gè)聽上去可能比較模糊,我們舉個(gè)例子來說明,假定亂碼形式是"à??í",嘗試多種B和A來看字符形式。我們先使用編輯器,以UltraEdit為例,然后使用Java編程來看。

使用UltraEdit

UltraEdit支持編碼轉(zhuǎn)換和切換查看編碼方式,也支持文件的二進(jìn)制顯示和編輯,所以我們以UltraEdit為例,其他一些編輯器可能也有類似功能。

新建一個(gè)UTF-8編碼的文件,拷貝"à??í"到文件中。使用編碼轉(zhuǎn)換,轉(zhuǎn)換到windows-1252編碼,功能在 "文件"->"轉(zhuǎn)換到"->"西歐"->WIN-1252。
轉(zhuǎn)換完后,打開十六進(jìn)制編輯,查看其二進(jìn)制形式,如下圖所示:

可以看出,其形式還是à??í,但二進(jìn)制格式變成了 C0 CF C2 ED。這個(gè)過程,相當(dāng)于假設(shè)B是windows-1252。這個(gè)時(shí)候,再按照多種編碼格式查看這個(gè)二進(jìn)制,在UltraEdit中,關(guān)閉十六進(jìn)制編輯,切換查看編碼方式為GB18030,功能在 "視圖"->"查看方式(文件編碼)"->"東亞語言"->GB18030,切換完后,同樣的二進(jìn)制神奇的變?yōu)榱苏_的字符形式 "老馬",打開十六進(jìn)制編輯器,可以看出,二進(jìn)制還是C0 CF C2 ED,這個(gè)GB18030相當(dāng)于假設(shè)A是GB18030。

這個(gè)例子我們碰巧第一次就猜對(duì)了。實(shí)際中,我們可能要做多次嘗試,過程是類似的,先進(jìn)行編碼轉(zhuǎn)換(使用B編碼),然后使用不同編碼方式查看(使用A編碼),如果能找到看上去對(duì)的形式,就恢復(fù)了。下圖列出了主要的B編碼格式,對(duì)應(yīng)的二進(jìn)制,按A編碼解讀的各種形式。


可以看出,第一行是正確的,也就是說原來的編碼其實(shí)是A即GB18030,但被錯(cuò)誤解讀成了B即Windows-1252了。

使用Java

關(guān)于使用Java我們還有很多知識(shí)沒有介紹,但一些讀者已經(jīng)有很好的Java知識(shí),所以本文一并列出相關(guān)代碼。

Java中處理字符串的類有String,String中有我們需要的兩個(gè)重要方法:

  • public byte[] getBytes(String charsetName),這個(gè)方法可以獲取一個(gè)字符串的給定編碼格式的二進(jìn)制形式
  • public String(byte bytes[], String charsetName),這個(gè)構(gòu)造方法以給定的二進(jìn)制數(shù)組bytes按照編碼格式charsetName解讀為一個(gè)字符串。

將A看做GB18030,B看做Windows-1252,進(jìn)行恢復(fù)的Java代碼如下所示:

String str = "à??í";
String newStr = new String(str.getBytes("windows-1252"),"GB18030");
System.out.println(newStr);

先按照B編碼(windows-1252)獲取字符串的二進(jìn)制,然后按A編碼(GB18030)解讀這個(gè)二進(jìn)制,得到一個(gè)新的字符串,然后輸出這個(gè)字符串的形式,輸出為"老馬"。

同樣,這個(gè)一次碰巧就對(duì)了,實(shí)際中,我們可以寫一個(gè)循環(huán),測(cè)試不同的A/B編碼中的結(jié)果形式,代碼如下所示:

public static void recover(String str) 
        throws UnsupportedEncodingException{
    String[] charsets = new String[]{"windows-1252","GB18030","Big5","UTF-8"};
    for(int i=0;i<charsets.length;i++){
        for(int j=0;j<charsets.length;j++){
            if(i!=j){
                String s = new String(str.getBytes(charsets[i]),charsets[j]);
                System.out.println("---- 原來編碼(A)假設(shè)是: "+charsets[j]+", 被錯(cuò)誤解讀為了(B): "+charsets[i]);
                System.out.println(s);
                System.out.println();    
            }
        }
    }
} 

以上代碼使用不同的編碼格式進(jìn)行測(cè)試,如果輸出有正確的,那么就可以恢復(fù)。

恢復(fù)的討論

可以看出,這種嘗試需要進(jìn)行很多次,上面例子嘗試了常見編碼GB18030/Windows 1252/Big5/UTF-8共十二種組合。這四種編碼是常見編碼,在大部分實(shí)際應(yīng)用中應(yīng)該夠了,但如果你的情況有其他編碼,可以增加一些嘗試。

不是所有的亂碼形式都是可以恢復(fù)的,如果形式中有很多不能識(shí)別的字符如??,則很難恢復(fù),另外,如果亂碼是由于進(jìn)行了多次解析和轉(zhuǎn)換錯(cuò)誤造成的,也很難恢復(fù)。

小結(jié)

上節(jié)和本節(jié)介紹了編碼的知識(shí),亂碼的原因及恢復(fù)方法,這些都是與語言無關(guān)的。

接下來,是時(shí)候看看在Java中如何表示和處理字符了,我們知道Java中用char類型表示一個(gè)字符,但在第三節(jié)我們提到了一個(gè)問題,即"字符類型怎么也可以進(jìn)行算術(shù)運(yùn)算和比較?"。

我們需要對(duì)Java中的字符類型有一個(gè)更為清晰和深刻的理解。


未完待續(xù),查看最新文章,敬請(qǐng)關(guān)注微信公眾號(hào)“老馬說編程”(掃描下方二維碼),深入淺出,老馬和你一起探索Java編程及計(jì)算機(jī)技術(shù)的本質(zhì)。原創(chuàng)文章,保留所有版權(quán)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • 為什么要編碼 不知道大家有沒有想過一個(gè)問題,那就是為什么要編碼?我們能不能不編碼?要回答這個(gè)問題必須要回到計(jì)算機(jī)是...
    艾小天兒閱讀 17,415評(píng)論 0 2
  • 編碼問題一直困擾著開發(fā)人員,尤其在 Java 中更加明顯,因?yàn)?Java 是跨平臺(tái)語言,不同平臺(tái)之間編碼之間的切換...
    x360閱讀 2,495評(píng)論 1 20
  • 晚間在鄉(xiāng)下聚餐。飯畢,在小村莊中信步閑逛,不覺間便晃上一條通往水庫的鄉(xiāng)間馬路。水泥路面可并行兩車,頗為寬闊,可能因...
    隨風(fēng)的心晴閱讀 118評(píng)論 0 0
  • PS:本文面向的讀者是已經(jīng)接觸過AutoLayout的,如果你還未接觸過,那本文內(nèi)容可能不大容易消化,關(guān)掉本頁或點(diǎn)...
    Jerry在種草閱讀 839評(píng)論 3 7
  • 天氣越來越?jīng)觯藗兯坪醪]有因?yàn)榧竟?jié)的變化而有所影響,依舊充滿歡聲笑語,依舊充滿調(diào)侃,依舊充滿虛情假意。 我有些厭...
    野鳥閱讀 801評(píng)論 4 1