python編碼問題

寫python的過程中經常出現各種蛋疼的編碼問題,于是通過上網查資料,自己做實驗,想徹底搞清楚這個問題。

編碼和解碼的理解

計算機是不認識字符的,計算機只認識二進制的01串,那么字符要存儲在計算機中,首先要做的就是把字符用二進制的01串來表示,這就是所謂的編碼(encode);當我們要閱讀存儲在計算機中的字符時,計算機就需要把二進制的01串轉換成我們可以讀的字符,這就是解碼(decode);所以,我們遇到encode error, 一般是計算機需要把某個字符進行存儲時,使用的編碼找不到該字符對應的01串,而我們遇到decode error時,一般是計算機讀取以01串存儲的數據,準備轉化成我們可識別的字符時,使用的編碼識別不了01串。不論是從字符轉化成二進制的01串進行存儲,還是從二進制的01串轉化成我們可讀的字符,都需要一個對應的轉化表,也就是哪個字符對應哪個01串,關于這樣的對應關系有很多種(比如utf-8, gbk等等),就稱為編碼方式(簡稱編碼,名詞)。顯然,每種編碼方式可以編碼的字符都是有限的,那么一種編碼方式可以編碼的字符集就稱作字符集吧。

如果我們用一種編碼方式 A 進行encode,用另一種編碼方式B進行decode,以“ 我 ” 這個漢字為例,那么就會出現三種情況:一,A和B所使用的字符集中都可以找到“ 我 ” 這個字符,而且A和B表示“ 我 ” 這個字的01串也一樣,所以“ 我 ” 這個字的編碼和解碼就不會存在問題;二,A和B所使用的字符集中都可以找到“ 我 ” 這個字符,但是A和B表示“ 我 ” 這個字符的01串不一致,這時候A按照自己的編碼方式將" 我 “ 存儲到計算機中,B按照自己的編碼方式解讀A對“ 我 ” 這個字表示的01串時,就會出現兩種情況,一是可以解釋,但是顯然解釋是錯誤的,可能會對應到另外的字符,這就稱之為亂碼,我們會看到一堆無意義的字符,另一種是直接不能解釋,這時候程序會直接報錯,python中就是decode error; 第三種是B所使用的編碼集種直接無法找到“ 我 ”, 其表現和第二種一樣。

python中的編碼

一、python文件的編碼

python文件是由python語言解釋器進行解釋執行的,默認情況下python解釋器對python文件用ascii編碼方式進行解碼,因此如果python文件中包含中文字符,就會報錯,如下面test.py的代碼

# main 程序
def main():
    print('hello, world!')

main()

執行python test.py 會得到

 File "test.py", line 1
 SyntaxError: Non-ASCII character '\xe7' in file test.py on line 1, but no       
 encoding declared; see http://python.org/dev/peps/pep-0263/ for details

我們可以看到Non-ASCII character '\xe5' in file,也就是說沒有聲明編碼的情況下,python解釋器按照ascii解碼的時候不認識'\xe5',現在在這個文件的頭部加入編碼聲明,那么聲明為什么編碼呢?python解釋器讀的是這個文件,因此這個文件是按照什么編碼存儲的就按照什么編碼聲明,我的編輯器是utf-8編碼的,因此我聲明為utf-8, 代碼如下

# coding: utf-8

# main 程序
def main():
   print('hello, world')

main()

這次再執行這個文件,就正常輸出了 “hello, world", 剛才這個編碼問題就是我們的編輯器保存代碼使用的編碼和python解釋器解釋代碼使用的編碼不一致導致的,因此我們通過 # coding: utf-8 告訴python解釋器應該使用的編碼,這個問題就解決了。

二、python中的字符串和unicode

由于我使用的是python2.7, 因此,僅針對python2.7討論這個問題。python中有str和unicode兩種表示字符串的方式,他們均繼承自basestring, 但是卻是完全不同的兩個東東。str可以說并不是真正的字符串,而是已經經過編碼的二進制01串,而unicode確實真正的字符串。

# coding: utf-8

# main 程序
def main():
    u=u'大家好'
    s='大家好'
    print(len(u))
    print(len(s))

main()

這段代碼的輸出是3和9,3我們很好理解,本來就是3個漢字;為什么s的長度是9呢?就是因為s是'大家好'這三個漢字已經編碼得到的長度為9字節的01串。這三個漢字已經編碼了,那么使用什么編碼方式呢?就是編輯器所使用的編碼方式,在我這兒也就是utf-8, 我們再做一個實驗,對這個問題有更加深刻的理解。

# coding: utf-8

# main 程序
def main():
    u=u'大家好'
    print(u)

main()

我執行 python test.py,正常輸出 “ 大家好 ", 然后我想讓這個輸出保存到文件中,因此我執行 python test.py > out.txt, 于是問題出現了,輸出下面的錯誤

Traceback (most recent call last):
File "test.py", line 8, in <module>
main()
File "test.py", line 6, in main
print(u)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2:        
ordinal not in range(128)

為什么我直接輸出到終端的時候一切正常,當我想重定向到文件的時候出問題了呢?當我直接輸出至終端的時候,按照終端所使用的編碼來編碼‘大家好’這3個字符,而我的終端所使用的編碼是utf-8,和我的編輯器所使用的編碼一致,所以不存在問題。但是當我把這個輸出要重定向至文件的時候,就出問題了,因為文件本身并不規定編碼方式,我也沒有對這個字符串進行顯式編碼,因此python將采用默認的編碼方式,而python2.7的默認編碼是ascii,因此會出現UnicodeEncodeError,搞清楚問題以后,也就有辦法解決問題了,一種是我們對這個unicode字符串進行顯式編碼, 如下所示

# coding: utf-8

# main 程序
def main():
    u=u'大家好'
    print(u.encode('utf-8'))

main()

另一種是改變python的默認編碼為我們這個字符串的編碼, 如下所示

# coding: utf-8

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

# main 程序
def main():
    u=u'大家好'
    print(u)

main()

一般推薦第一種方式,盡量避免第二種方式,因為第二種方式只有當我們需要處理的編碼方式只有一種時有效,如果我們使用多種編碼方式,那么仍然會存在問題。我們再看一段代碼

# coding: utf-8

# main 程序
def main():
    s='大家好'
    print(s)

main()

對這段代碼執行python test.py直接輸出在終端和python test.py > out.txt 都可以得到預期效果,這又是為什么呢?這段代碼和之前代碼的區別是'大家好'這個字符串是str而不是unicode類型。我們知道str類型是已經編碼的01串,因此在輸出至終端或文件時不需要再進行編碼,所以不會出現之前遇到的問題。

三、思考

這樣看,貌似使用str類型更方便一些,省去了我們進行顯式的編碼了,實則恰恰相反,因為這種隱式的編碼方式很不利于代碼維護,雖然代碼暫時很僥幸,很容易的運行通過了,但是日后我們很難搞清楚這個str里面究竟是什么編碼,而且我們也看到通過len拿到的字符串長度也存在問題。而使用第一種unicode的方式,雖然我們寫代碼時需要多費些功夫,在輸出時需要顯式進行編碼,但是這樣也明確了這個字符串所采用的編碼,同時拿到的字符串長度也是準確的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 幾個基本概念 bit二進制位, 是計算機內部數據儲存的最小單位,11010100是一個8位二進制數。一個二進制位只...
    西電大俠閱讀 3,590評論 1 8
  • 閱讀python源代碼的時候不難發現許多文件開頭都有這樣一行內容: 如果想在python中使用中文,這樣的一行聲明...
    Fengya閱讀 777評論 0 50
  • 什么是編碼 任何一種語言、文字、符號等等,計算都是將其以一種類似字典的形式存起來的,比如最早的計算機系統將英文文字...
    隨風化作雨閱讀 1,562評論 1 2
  • 繼上一篇文章字符集和編碼詳解總結了常見字符編碼后,這篇文章會對python中常見的編碼問題進行分析和總結。由于py...
    __七把刀__閱讀 2,911評論 0 6
  • 2016-05-01 原創 星夏 我們憑什么活著? 家人的期望、自己的理想、該承擔的責任? 我不知道千...
    _小夭閱讀 493評論 0 0