Python的中文編碼問題

發表於2014-12-24

字串是Python中最常用的資料型別,而且很多時候你會用到一些不屬於標準ASCII字符集的字元,這時候程式碼就很可能丟擲UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xc4 in position 10: ordinal not in range(128)異常。這種異常在Python中很容易遇到,尤其是在Python2.x中,是一個很讓初學者費解頭疼的問題。不過,如果你理解了Python的Unicode,並在編碼中遵循一定的原則,這種編碼問題還是比較容易理解和解決的。

字串在Python內部的表示是unicode編碼,因此,在做編碼轉換時,通常需要以unicode作為中間編碼,即先將其他編碼的字串解碼(decode)成unicode,再從unicode編碼(encode)成另一種編碼。但是,Python 2.x的預設編碼格式是ASCII,就是說,在沒有指定Python原始碼編碼格式的情況下,原始碼中的所有字元都會被預設為ASCII碼。也因為這個根本原因,在Python 2.x中經常會遇到UnicodeDecodeError或者UnicodeEncodeError的異常。

關於Unicode

Unicode是一種字符集,它為每一種現代或古代使用的文字系統中出現的每一個字元都提供了統一的序列號,規定了符號的二進位制程式碼,但沒有規定這個二進位制程式碼應該如何儲存。也就是說:Unicode的編碼方式是固定的,但是實現方式根據不同的需要有跟多種,常見的有UTF-8、UTF-16和UTF-32等。更多的介紹大家可以參看維基百科:Unicode

為了能夠處理Unicode資料,同時相容Python某些內部模組,Python 2.x中提供了Unicode這種資料型別,通過decode和encode方法可以將其它編碼和Unicode編碼相互轉化,但同時也引入了UnicodeDecodeError和UnicodeEncodeError異常。。

常見的幾種編碼異常

Python中常見的幾種編碼異常有SyntaxError: Non-ASCII character、UnicodeDecodeError和UnicodeEncodeError等。下面依次舉例說明一下:

1、SyntaxError: Non-ASCII character

這種異常最不容易出現,也最容易處理,主要原因是Python原始碼檔案中有非ASCII字元,而且同時沒有宣告原始碼編碼格式,例如:

2、UnicodeDecodeError

這個異常有時候會在呼叫decode方法時出現,原因是Python打算將其他編碼的字元轉化為Unicode編碼,但是字元本身的編碼格式和decode方法傳入的編碼格式不一致,例如:

上面這段程式碼中字串s的編碼格式是utf-8,但是在使用decode方法轉化為Unicode編碼時傳入的引數是‘gb2312’,因此在轉化的時候丟擲UnicodeDecodeError異常。還有一種情況是在encode的時候:

3、UnicodeEncodeError

錯誤的使用decode和encode方法會出現這種異常,比如:使用decode方法將Unicode字串轉化的時候:

當然,除了上面列出的幾種出現異常的情況之外還有很多可能出現異常的例子,這裡就不在一一說明了。

解決方法

對於以上的幾個異常,有以下幾個處理的方法和原則。

1、遵循PEP0263原則,宣告編碼格式

PEP 0263 — Defining Python Source Code Encodings中提出了對Python編碼問題的最基本的解決方法:在Python原始碼檔案中宣告編碼格式,最常見的宣告方式如下:

其中<encoding name>是程式碼所需要的編碼格式,它可以是任意一種Python支援的格式,一般都會使用utf-8的編碼格式。

2、使用u’中文’替代’中文’

Python中有以上兩種宣告字串變數的方式,它們的主要區別是編碼格式的不同,其中,str1的編碼格式和Python檔案宣告的編碼格式一致,而str2的編碼格式則是Unicode。如果你要宣告的字串變數中存在非ASCII的字元,那麼最好使用str2的宣告格式,這樣你就可以不需要執行decode,直接對字串進行操作,可以避免一些出現異常的情況。

3、Reset預設編碼

Python中出現這麼多編碼問題的根本原因是Python 2.x的預設編碼格式是ASCII,所以你也可以通過以下的方式修改預設的編碼格式:

這種方法是可以解決部分編碼問題,但是同時也會引入很多其他問題,得不償失,不建議使用這種方式。

4、終極原則:decode early, unicode everywhere, encode late

最後分享一個終極原則:decode early, unicode everywhere, encode late,即:在輸入或者宣告字串的時候,儘早地使用decode方法將字串轉化成unicode編碼格式;然後在程式內使用字串的時候統一使用unicode格式進行處理,比如字串拼接、字串替換、獲取字串的長度等操作;最後,在輸出字串的時候(控制檯/網頁/檔案),通過encode方法將字串轉化為你所想要的編碼格式,比如utf-8等。

按照這個原則處理Python的字串,基本上可以解決所有的編碼問題(只要你的程式碼和Python環境沒有問題)。。。

5、升級Python 2.x到3.x

額,最後一個方法,升級Python 2.x,使用Python 3.x版本。。這樣說主要是為了吐槽Python 2.x的編碼設計問題。當然,升級到Python 3.x肯定可以解決大部分因為編碼產生的異常問題。畢竟Python 3.x版本對字串這部分還是做了相當大的改進的,具體的下面會說。。。。

Python 3.x中的Unicode

在Python 3.0之後的版本中,所有的字串都是使用Unicode編碼的字串序列,同時還有以下幾個改進:

  • 1、預設編碼格式改為unicode
  • 2、所有的Python內建模組都支援unicode
  • 3、不再支援u’中文’的語法格式

所以,對於Python 3.x來說,編碼問題已經不再是個大的問題,基本上很少遇到上述的幾個異常。關於Python 2.x str&unicode和Python 3.x str&bytes的更多說明和對比,大家可以看一下:Python中字元編碼的總結和對比

參考文章

相關文章