字符集錯誤轉換導致的問題
UTF-8格式編碼的字節流,按GBK字符集轉換為字符串,會出現亂碼,這很正常。但將其重新轉為字節流,再用UTF-8字符集轉為字符串,還是亂碼。這就讓我產生了疑惑,雖然使用錯誤的字符集必然導致亂碼,但字節的信息並沒有改變,因此再轉為字節流,用正確的字符集解碼,應該得到正常的字符串。但事實是,被錯誤字符集轉換過的字符串,無法恢復到原來的字符集。
問題的根本原因
造成該問題的根源是字節發生了變化。GBK或UTF-8遇到無法解析的字符時,會使用特殊的字符代替,因此造成原有字節信息的丟失,無法恢復。
錯誤轉換的分析
UTF-8 → GBK
對於一串UTF-8編碼的字節流,使用GBK進行解碼。連續兩個大於127的字節被認為是一個GBK編碼的字符;若只讀到一個大於127的字節,便發生錯誤,無法解析。此時,用字符'?'代替錯誤字節,ASCII碼是63。
以“樊”字為例,UTF-8編碼使用三個字節表示該字符,字節碼為[11100110, 10101000, 10001010]([e6, a8, 8a])。使用GBK解碼時,讀到第一個字節大於127,則取兩個字節解析為一個GBK字符。前兩個字節e6 8a被解析為GBK字符——妯。 第三個字節無法解析,所以賦值為?,最后的結果是 妯?。
可以看出,最后一個字節的信息丟失了,由8a變成3F,即使把結果再轉換為字節流,也無法用utf-8字符集正確解析了。
GBK → UTF-8
對於一串GBK編碼的字節流,使用UTF-8解碼。UTF-8對於字節的格式有嚴格要求,當解析某個字符失敗時,使用'�'(UTF-8編碼為EF BF BD)代替。
繼續以“樊”字為例,其GBK字節碼為[10110111, 10101110]([B7, AE])。使用UTF-8解碼時,根據規則,要求10開頭的字節之前,必須有字節標識一個字符的長度,所以兩個字節都無法解析。最后的字符串是��。
可以看出,所有的字節信息都丟失了,因此無法再使用GBK解析該字符串。
注意,UTF-8用�替換,是以字符為單位的。例如[11100110, 10101000, 01000001]使用UTF-8解碼,得到的結果是�A,而不是��A。根據第一個字節的格式,UTF-8期望將三個字節轉換為一個字符。但最后一個字節不符合要求,所以前兩個字節被一個�代替。而不是每個字節都被�代替。