Ruby實例編程說明字符編碼,解決亂碼問題

Ruby实例编程说明字符编码,解决乱码问题

日常編碼中,你是不是常見到亂碼問題,那麼亂碼是怎麼來的呢,今天蟲蟲就以Ruby為例來,說明字符編碼、底層的字節碼以及各種字符編碼轉化,最後說下怎麼解決亂碼問題。

我們以一個Ruby拋出的錯誤異常為開頭,某年某月某日,你正用irb學習Ruby突然屏幕上拋出了一個異常:

Encoding::InvalidByteSequenceError: "\\xFE" on UTF-8

或者 "they're"被顯示為"they’re"。

所以,當出現編碼錯誤,或者亂碼時候,我們怎麼排查問題出在哪裡?如何解決它?

什麼是編碼?

如果你瞭解字符串以及編碼之間的關係,其實這些問題很容易解決。

我們可以將字符串視為字節數組或數字:

2.1.4 :002 > "chongchong!".bytes

=> [99, 104, 111, 110, 103, 99, 104, 111, 110, 103, 33]

Ruby實例編程說明字符編碼,解決亂碼問題

在這種編碼中,99表示c,104表示h,…,33表示!。

當你使用英語中不常見的字符時,情況會變得更加複雜:

2.1.4 :007 > "chongchong".bytes

=> [240, 159, 166, 139, 99, 104, 111, 110, 103, 99, 104, 111, 110, 103]

Ruby實例編程說明字符編碼,解決亂碼問題

現在很難判斷哪個數字代表哪個字符了把。這個蝴蝶字符使用一組字節[240, 159, 166, 139]表示,而不是一個字節。但字節和符號之間會存在著一定的對應關係。這種定義字節和符號(字符串)之間的對應關係,我們叫編碼。

不同編碼轉換

在不同編碼環境下,一個字節可能呈現出不同的符號表現:

2.1.4 :021 > str = "蟲蟲,秊﨟蘒!".encode("GBK");

2.1.4 :022 > str.encode("UTF-8")

=> "蟲蟲,秊﨟蘒!"

Ruby實例編程說明字符編碼,解決亂碼問題

2.1.4 :027 > str.bytes

=> [179, 230, 179, 230, 163, 172, 253, 158, 254, 72, 254, 73, 33]

Ruby實例編程說明字符編碼,解決亂碼問題

GBK的字串,我們看看在西文編碼的ISO-8859-5下會是怎麼顯示呢?

str.force_encoding("ISO-8859-5"); str.encode("UTF-8")

2.1.4 :003 > str.force_encoding("ISO-8859-5"); str.encode("UTF-8")

=> "ГцГцЃЌ§\\u009EўHўI!"

Ruby實例編程說明字符編碼,解決亂碼問題

2.1.4 :004 > str.bytes

=> [179, 230, 179, 230, 163, 172, 253, 158, 254, 72, 254, 73, 33]

Ruby實例編程說明字符編碼,解決亂碼問題

我們可以看到,字節碼沒有變化,但是看起來亂碼了。改變編碼會改變字符的顯示方式,但是不會改變字節碼,不同的編碼,有不同的字節碼解析方式。

從GB2312到GBK,選擇UTF8

另外我們需要注意的是,每一種編碼的數量都有限量,有些編碼數量少,會導致其他編碼轉過來會不支持,比如GBK是GB2312的擴展集合支持21886個字符,而GB2312只支持7445個字符。注意UTF8和UTF16是全字符集,支持所有編碼的字符,這就是為什麼建議大家寫代碼一定都用UTF8的原因,當然在Windows下微軟屎一樣的,堅持自己要帶UTF8 BOM,還不能方便的轉換,所以也很討厭。

2.1.4 :005 > str.force_encoding("GB2312"); str.encode("UTF-8")

Encoding::InvalidByteSequenceError: "\\xFD" followed by "\\x9E" on GB2312

from (irb):5:in `encode'

from (irb):5

from /home/lz/.rvm/rubies/ruby-2.1.4/bin/irb:11:in `

'

Ruby實例編程說明字符編碼,解決亂碼問題

後面那三個字,在GB2312下沒有編碼,所以報錯了。

Ruby實例編程說明字符編碼,解決亂碼問題

我們將編碼修改為GB18030,這是一種比GBK更大的中文字符集,顯示就沒問題了。

我們也可以給encode函數添加invalid和undef兩個參數,這樣當無法解釋時候會用默認的?來代替,就不會報錯了。

"chongchong,秊﨟蘒!".encode("GB2312", invalid: :replace, undef: : replace)

=> "chongchong\\x{A3AC}???!"

Ruby實例編程說明字符編碼,解決亂碼問題

我們常見的編碼問題有兩種現象,一種是編碼不對,就是完全亂碼,亂顯示隨機字符,還有一種就編碼不足以解析字符集會有很多的??的。

Ruby實例編程說明字符編碼,解決亂碼問題

當你對字符轉換編碼時,很多時候,你不能獲得足夠的信息,你不知道究竟是哪一個被替換成了?但是你起碼知道選的編碼沒有錯,只不過不足以顯示所有字符而已,你換一種字符集更多的編碼就正常了。比如GB2312->GBK-> GB18030

總結

在文章中,我們目前用到了三個字符串對象方法來幫我們處理編碼問題:

ecode,將字符串轉換為另一種編碼(在新編碼中將字符轉換為相應字符)

bytes,它將顯示組成字符串的字節碼(整數數組)

force_encoding,強制將一種編碼轉換為一種編碼,用這宗編碼來重新解析內容。

其實上編碼問題在各種語言都存在,這兒蟲蟲選擇了Ruby中三個類方法來作為實例演示,其他語言中也有類似的方法和函數,比如:

golang中的golang.org/x/text/encoding/simplifiedchinese包。

python中的enode()和decode(),XX_name(XX為編碼)方法。

Perl中encode包中也有enode()和decode()。

Java中有getBytes(),charset類有enode()和decode()方法。


分享到:


相關文章: