在我們處理文件或者處理程序字符時,時不時會遇到亂碼的情況,而且這些亂碼的情況讓人很困惑,大多時候都是CV某度一下,看看有沒有相關類似情況的博文出現,如果有那就按照博文上的方式一步一步去解決就好,如果沒找到,很多人就直接emo了。其實,亂碼聽起來挺復雜的,其實也不是非常難解決。今天來聊一聊關于如何解決字符的編碼和亂碼問題。
編碼是信息從一種形式或格式轉換為另一種形式的過程,也稱為計算機編程語言的代碼簡稱編碼。用預先規定的方法將文字、數字或其它對象編成數碼,或將信息、數據轉換成規定的電脈沖信號。編碼在電子計算機、電視、遙控和通訊等方面廣泛使用。編碼是信息從一種形式或格式轉換為另一種形式的過程。解碼,是編碼的逆過程。
在日常的開發中,有非常多的編碼的格式,比如GBK、UTF-8、ASCII等等,很多人都不清楚這些編碼格式之間有什么區別聯系,只知道編碼和解碼的格式用同一種就不會出現亂碼問題。其實在計算機軟件領域編碼就劃分為兩大類,一種是Unicode編碼,另一種是非Unicode編碼。
常見的非Unicode編碼有,ASCII、GBK、GB18030、ISO8859-1、windows-1252 等;
我們都知道世界第一臺計算機的誕生在美國,當時的作者沒考慮那么多,只考慮了美國的需求(美國大概使用128個字符),所以規定了當時128個字符的二進制表示的方法,也就是一套標準,即ASCII,全稱American Standard Code for Information Interchange,譯為美國信息互換標準代碼。
計算機存儲的最小單位是byte,即8位,128個字符剛好可以使用7位表示,在ASCII中,最高位設置為0,另外7位可以使用0~127來表示字符,其中在ASCII編碼中規定了0~127各個數字代表的含義,基本上能覆蓋鍵盤上的所有字符。
需要注意的是,在ASCII中存在少數比較特殊不可打印的字符,常用不可打印的字符有
ASCII碼對于美國來說是夠用了,但世界上的國家那么多,字符和語言也不一樣,于是,各個國家的各種計算機廠商就發明了各種各種的編碼方式以表示自己國家的字符,為了保持與ASCII碼的兼容性,一般都是將最高位設置為1。也就是說,當最高位為0時,表示的是標準的ASCII碼,為1時就是各個國家擴展自己的字符的編碼。在這些擴展的編碼中,在西歐國家中流行的是ISO 8859-1和Windows-1252,在中國是GB2312、GBK、GB18030,等會挨個介紹這些編碼的區別。
ISO 8859-1又稱Latin-1,它是使用一個字節表示一個字符,其中0~127與ASCII一樣,128~255規定了不同的含義。在128~255中,128~159表示一些控制字符,160~255表示一些西歐字符,這些字符在中國也不常用,就不一一介紹了。
ISO 8859-1雖然號稱是用于西歐國家的統一標準,但它連歐元()這個符號都沒有,因為歐元這個符號比較晚,而ISO 8859-1標準比較早。所以實際中使用更為廣泛的是Windows-1252 編碼,這個編碼與ISO 8859-1基本是一樣的,區別只在干數字128~159。HTML5甚至明確規定,如果文件聲明的是ISO 8859-1編碼,它應該被也可以看作Windows-1252編碼。為什么要這樣呢?因為大部分人搞不清楚ISO 8859-1和Windows-1252的區別,當他說ISO 8859-1的時候,其實他指的是Windows-1252,所以標準干脆就這么強制規定了。Windows-1252使用其中的一些數字表示可打印字符
這三種編碼格式相信國內的開發者都不陌生了,這三種也就是中文字符顯示的編碼格式,那這三種之間有什么區別和聯系呢?
美國和西歐字符用一個字節就夠了,但中文顯然是不夠的。中文第一個標準是GB2312。GB2312標準主要針對的是簡體中文常見字符,包括約7000個漢字和一些罕用詞和繁體字。
GB2312固定使用兩個字節表示漢字,在這兩個字節中,最高位都是1,如果是0,就認為是ASCII字符。在這兩個字節中,其中高位字節范圍是0xA1~0xF7,低位字節范圍是0xA1~0xFE。
GBK建立在GB2312的基礎上,向下兼容GB2312,也就是說,GB2312編碼的字符和二進制表示,在 GBK編碼里是完全一樣的。GBK增加了14000多個漢字,共計約21000個漢字,其中包括繁體字。
GBK同樣體里固定的兩個字節表示,其中高位字節范圍是0x81~0xFE,低位字節范圍是0x40~0x7F和Ox80~0xFE。
需要注意的是,低位字節是從Ox40(也就是64)開始的,也就是說,低位字節的最高位可能為0。那怎么知道它是漢字的一部分,還是一個ASCII字符呢?其實很簡單,因為漢字是用固定兩個字節表示的.在解析二進制流的時候,如果第一個字節的最高位為1,那么就將下一個字節讀進來一起解析為一個漢字,而不用考慮它的最高位,解析完后,跳到第三個字節繼續解析。
GB18030向下兼容GBK,增加了55000多個字符,共76000多個字符,包括了很多少數民族字符,以及中日韓統一字符。
用兩個字節已經表示不了GB18030中的所有字符,GB18030使用變長編碼,有的字符是兩個字節,有的是四個字節。在兩字節編碼中,字節表示范圍與GBK一樣。在四字節編碼中,第一個字節的值為0x81~0xFE,第二個字節的值為0x30~0x39,第三個字節的值為0x81~0xFE,第四個字節的值為0x30~0x39。
解析二進制時,如何知道是兩個字節還是4個字節表示一個字符呢?看第二個字節的范圍,如果是0x30~0x39就是4個字節表示,因為兩個字節編碼中第二個字節都比這個大。
如果說上述的編碼能表示中文、英語等所需的字符,那世界那么,國家語言多種多樣,每個國家都基于ASCII去實現一套編碼標準,那么將會出現成千上萬套編碼。那么就沒有一套世界統一的標準?有,這就是Unicode編碼!
Unicode做了一件事,就是給世界上所有字符都分配了一個唯一的數字編號,這個編號范圍從0x000000~0x10EEEF,包括110多萬。但大部分常用字符都在0x0000~0xEEEF之間,即65536個數字之內。每個字符都有一個Unicode編號,這個編號一般寫成十六進制,在前面加U+。大部分中文的編號范圍為U+4E00~U+9FFF。
簡單理解,Unicode主要做了這么一件事,就是給所有字符分配了唯一數字編號。它并沒有規定這個編號怎么對應到二進制表示,這是與上面介紹的其他編碼不同的,其他編碼都既規定了能表示哪些字符,又規定了每個字符對應的二進制是什么,而Unicode本身只規定了每個字符的數字編號是多少。目前常用的編碼方案有UTF-8、UTF-16以及UTF-32。
UTF-8使用變長字節表示,每個字符使用的字節個數與其Unicode編號的大小有關,編號小的使用的字節就少,編號大的使用的字節就多,使用的字節個數為1~4不等。小于128的,編碼與ASCII碼一樣,最高位為0。其他編號的第一個字節有特殊含義,最高位有幾個連續的1就表示用幾個字節表示,而其他字節都以10開頭。
對于一個Unicode編號,具體怎么編碼呢?首先將其看作整數,轉化為二進制形式(去掉高位的0).然后將二進制位從右向左依次填入對應的二進制格式x中,填完后,如果對應的二進制格式還有沒填的x則設為0。
UTF-16使用變長字節表示:
1)對于編號在U+0000~U+FFFF的字符(常用字符集),直接用兩個字節表示。需要說明的是, U+D800~U+DBFF的編號其實是沒有定義的。
2)字符值在U+10000~U+10FFFF的字符(也叫做增補字符集),需要用4個字節表示。前兩個字節叫高代理項,范圍是U+D800~U+DBFF;后兩個字節叫低代理項,范圍是U+DC00~U+DFFF。數字編號和這個二進制表示之間有一個轉換算法,這里就不詳細介紹了。
區分是兩個字節還是4個字節表示一個字符就看前兩個字節的編號范圍,如果是U+D800~U+DBFF,就是4個字節,否則就是兩個字節。
這個最簡單,就是字符編號的整數二進制形式,4個字節。
但有個細節,就是字節的排列順序,如果第一個字節是整數二進制中的最高位,最后一個字節是整數二進制中的最低位,那這種字節序就叫“天端”(Big Endian,BE),否則,就叫“小端”(Little Endian. LE)。對應的編碼方式分別是UTF-32BE和UTF-32LE。
可以看出,每個字符都用4個字節表示,非常浪費空間,實際采用的也比較少。
Unicode給世界上所有字符都規定了一個統一的編號,編號范圍達到110多萬,但大部分字符都在65536以內。Unicode本身沒有規定怎么把這個編號對應到二進制形式。
UTE-32/UTE-16/UTE-8都在做一件事,就是把Unicode編號對應到二進制形式,其對應方法不同而已。UTF-32使用4個字節,UTF-16大部分是兩個字節,少部分是4個字節,它們都不兼容ASCII編碼,都有字節順序的問題。UTF-8使用1~4個字節表示,兼容ASCII編碼,英文字符使用1個字節,中文字符大多用3個字節。
有了統一的Unicode編碼后,就可以兼容不同類型的編碼格式了,比如中文“西”字:
編碼方式 | 十六進制 | 編碼方式 | 十六進制 |
GBK | cef7 | UTF-8 | %u897F |
Unicode | \u897f | UTF-16 | 897f |
在不同的編碼格式之間是如何通過Unicode編碼進行兼容的呢?那就必須通過編碼去轉換,我們可以認為每種編碼都有一個映射表,存儲其特有的字符編碼和Unicode編號之間的對應關系,這個映射表是一個簡化的說法,實際上可能是一個映射或轉換方法。
編碼轉換的具體過程可以是:一個字符從A編碼轉到B編碼,先找到字符的A編碼格式,通過A的映射表找到其Unicode編號,然后通過Unicode編號再查B的映射表,找到字符的B編碼格式。通過這樣轉換,就可以實現不同編碼格式的兼容了。
舉例來說,“西”從GBK轉到UTF-8,先查GB18030->Unicode編號表,得到其編號是\u897f,然后查Uncode編號->UTF-8表,得到其UTF-8編碼: %u897F。
上面介紹了編碼,現在我們來看一下亂碼,產生亂碼一般無非兩個原因:一種就是比較簡單的錯誤解析,另外一種就是比較復雜的,在錯誤解析的基礎上還進行了編碼轉換。
一個英國人使用Windows-1252編碼格式寫了一個文件發送給一個中國人,然后中國人使用GBK解碼打開,最后看到的這個文件就是亂碼的;
這種情況下,之所以看起來是亂碼,是因為看待或者說解析數據的方式錯了。只要使用正確的編碼方式進行解讀就可以糾正了。很多文件編輯器,如EditPlus、NotePad++都有切換查看編碼方式的功能,有的瀏覽器也有切換查看編碼方式的功能,如火狐瀏覽器,在菜單“查看”→“文字編碼”中即可找到該功能。
切換查看編碼的方式并沒有改變數據的二進制本身,而只是改變了解析數據的方式,從而改變了數據看起來的樣子,這與前面提到的編碼轉換正好相反。很多時候,做這樣一個編碼查看方式的切換就可以解決亂碼的問題,大多數僅是簡單解析錯誤可以使用此方法解決。
如果怎么改變查看方式都不對,那很有可能就不僅僅是解析二進制的方式不對,而是文本在錯誤解析的基礎上還進行了編碼轉換。我們舉個例子來說明:
比如“西”字,本來的編碼格式是GBK,編碼(十六進制)是cef7。
這個二進制形式被錯誤當成了Windows-1252編碼,解讀成了字符“?÷”。
隨后這個字符進行了編碼轉換,轉換成了UTF-8編碼,形式還是“?÷”,但二進制變成了 11111111111111111111111111111111。
這個時候再按照GBK解析,字符就變成了亂碼形式“?÷”,而且這時無論怎么切換查看編碼的方式,這個二進制看起來都是亂碼。
這種情況是亂碼產生的主要原因。
這種情況其實很常見,計算機程序為了便于統一處理,經常會將所有編碼轉換為一種方式,比如UTF-8,在轉換的時候,需要知道原來的編碼是什么,但可能會搞錯,而一旦搞錯并進行了轉換,就會出現這種亂碼。這種情況下,無論怎么切換查看編碼方式都是不行的。
對付亂碼的方法,如果是簡單的方法,可以先使用編輯器試著重新解析,看看能不能解析回到正確的編碼;但是如果遇到稍微復雜一點就不行,比如上述提到的錯誤的解析加上轉換,那個用編輯器就不能找回正確的方法,這里推薦使用程序來解決;這里使用錯誤解析成“?÷”的亂碼字符,我們來找回它正確的編碼:
首先我們寫個方法,先用個數組將所有的編碼格式放進去(我這里演示就簡單列舉幾個),然后使用循環去解析,再從輸出的結果中,查找符合原編碼的字符及對應編碼。
程序是這樣的:
最后的運行結果為:
最后我們得到的結果為“?÷”的亂碼字符原編碼是GBK,被錯誤解析為Windows-1252,這樣我們就算是找到了字符的原編碼了。
根據這個程序就能反著找到原來的編碼,因為我們實際應用的編碼格式有限,所以這種暴力反查找的方式還是很有用的,速度也很快。
當然,能找到的對應的原編碼都是一些簡單和不算非常復雜的亂碼文件,如果文件被多次錯誤解析和多次格式轉換,那其反破解原編碼的難度無異于去破解無固定的保險柜密碼。
通過本文可以清晰了解到計算機軟件領域編碼的分類,共分為非Unicode編碼和Unicode編碼,非Unicode編碼主要是以ASCII體系為主,而Unicode編碼最多應用的方案是UTF-8,這些不同的編碼類型之間是可以通過一定的規則相互轉換的。
編碼和解碼使用的不是同一套編碼格式,會導致亂碼現象的產生,因此在產生亂碼時,我們一是可以借助一些編輯器或瀏覽器去更換編碼來找到原先的格式,二是可以借助程序工具進行去暴力匹配,這種方法也比較直接有效。
——參考致謝《Java編程的邏輯》
IT之家(www.ithome.com):Win10預覽版9860:通知中心詳細解剖
Win10之家報道,Windows10預覽版已升級至Build 9860,帶來7000多項改進以及各種與Windows Phone 8.1類似的新功能,包括通知中心,節電模式,流量感知等。本次讓我們來詳細的探索通知中心的各類細節。
在Windows8時代,Modern界面應用的通知是一個很棘手的問題。一旦用戶錯過通知,便需要在Modern界面中尋找發送通知的應用,費力且費時。
微軟著手修復問題方法便是將Windows Phone 8.1中的通知中心并入Win10中。在系統托盤圖標附近可以找到通知中心圖標,圖標樣式與WP8.1相同。
單擊圖標便可查看通知消息,排列順序與推送時間相同,點擊推送內容便會切換到相關應用,點擊右上角“CLEAR ALL”(全部清除)會清除全部消息但不會關閉窗口,也可以通過鼠標拖拽清除所選推送,與Windows Phone手機效果基本相同。
與WP8.1手機不同的是,通知中心圖標會一直存在,有未讀消息時圖標樣式會改變,一顆藍色小星星會出現在圖標中,而不是類似WP中接到推送才顯示圖標。右鍵點擊通知中心圖標,能夠看到“隱藏通知”選項,可以在“1/3/8個小時”中選擇消息隱藏多久。
在WP8.1手機中,已經有工具可以使消息長期顯示在通知中心,類似功能將來在Win10中也有可能出現,等同于多了一種創建快捷方式的途徑。
目前的通知中心功能不完整,存在英文顯示,甚至還有部分亂碼存在,但微軟會持續完善Windows10以改善用戶體驗。(Via: Neowin)