Python2 新手 編碼問題 吐血總結


什么是編碼

  任何一種語言、文字、符號等等,計算都是將其以一種類似字典的形式存起來的,比如最早的計算機系統將英文文字轉為數字存儲(ASCII碼),這種文字與數字(或其他)一一對應的關系我們稱之為編碼。由於ASCII碼只包含了大小寫英文字母、數字和一些符號,顯然當計算機推廣到世界之后隨着語種增多,這套編碼並不適用,於是中國針對中文推出了GB2312碼,但是多語言時,又不行了,於是就出現了強大的Unicode(萬國碼)。但是由於Unicode存儲性能問題,在純英文時存儲效率要遠低於ACSII碼,於是又出現了現在的UTF-8編碼(8-bit Unicode Transformation Format),可以看做是Unicode的加強版,通過可變長度的編碼來使存儲最優,而且UTF-8編碼包含了ASCII碼,這一點非常重要。
  python處理文本時的中間編碼為Unicode,於是就有了decode和encode,前者將unicode以外的字符串解碼為unicode,后者將unicode編碼為指定編碼。

當你輸入字符串時

  首先,當你在python代碼中輸入一個字符串時候,它是以什么編碼形式被保存的呢?

1.如果輸入了一串純英文,數字,或英文狀態下的標點符號,那么無論有沒有在代碼最前面做編碼申明(如"# -- coding:utf-8 --"),字符串都是由ASCII碼存儲的,原因很簡單,ascii碼只支持英文,占用性能與空間小。

  • 此時,該字符串可以隨意decode(解碼)和encode(編碼),不會報錯,甚至不會進行任何變動,永遠都是ascii碼,type類型是str
  • 如果在輸入該字符串時加入了Unicode申明,即 u"balabala",那么此時字符串type格式為Unicode,可以隨意encode,不可decode,但是無論encode成什么,python還是都會以ascii的形式存儲

2.如果輸入了中文,那么情況一下子就變得復雜起來。此時必須進行編碼申明,否則會拋出如下錯誤:“Non-ASCII character '\xe5' in file **,but no encoding declared”,意思就是你輸入了ASCII碼無法識別的東西,且沒有進行編碼申明,所以此時要在文件開頭進行編碼申明,完整版如下:

#!/usr/bin/python
# -*- coding: <encoding name> -*-

  此時 處填上編碼方式,不區分大小寫,其實只寫下面一行就行了,上面一行只是為了在linux系統里識別而已。很多人對這種在注釋中進行申明的方式很不習慣,也不解-*-是什么鬼,但是 PEP 263告訴我們,這樣只是為了好看而已。。。

  • 舉個例子,如果你申明了utf-8編碼,那么你輸入的任何帶有非英文(以及符合)非數字的字符串,都是utf-8編碼,我們可以通過 .decode('utf-8')的方式將其解碼為Unicode碼方便python處理,注意此時不僅其編碼編程Unicode,其類型也從str變成了Unicode。當然也可以使用unicode(string,'utf-8')的方式來將其解碼為unicode,Unicode函數與str函數的區別是前者嘗試用給定編碼(不給定時用ASCII)進行decode,而后者嘗試用ASCII(defaultencoding)進行encode,由於ASCII碼被utf-8碼包含,所以對於utf-8字符串,進行str()是沒有問題的,但是對於其他編碼文本進行str()則會報錯或是亂碼。
  • 如果輸入字符串時進行Unicode申明,如a=u"楊睿很帥",那么此時字符串編碼直接為unicode。可隨意進行encode,不可decode,不可str。

注意

1.chardet庫的detect方法可以得到字符串的編碼類型,當輸入字符串為unicode時程序報錯,有時候也會誤判,置信水平小魚0.7則不可輕信了。

2.上述只針對在非DOS中執行py文件時適用:如果是在IDLE中單步執行,則中文字符串是以系統默認編碼(windows-1252)保存;如果是在DOS界面中運行,則為GBK編碼,而且中文也必須是GBK編碼才可正常顯示,否則報錯。

3.選擇一款好的IDE,設置一款獨特的凸顯品味的字體與配色,能夠讓初學者前期愉快地被編碼問題搞崩,而不是惱火地崩掉,也能幫助你很好的管理代碼。(推薦PyCharm,有免費版)

設置默認編碼

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

  以上代碼將系統編碼由ASCII碼轉為UTF-8編碼。reload(sys)是因為import時將setdefaultencoding()方法刪除了,所以將其重新載入回來。為什么說ascii是系統默認編碼,因為當你使用str()給字符串encode或者是unicode()來decode時,都是默認使用了ASCII碼,因此經常會報出類似"UnicodeDecodeError: 'ascii' codec can't decode byte 0xd1 in position 0"的錯誤,原因就是字符串里摻雜了中文,前面說到ascii碼是不支持中文的。如果把系統默認編碼設置為utf-8,就不會出現這樣的問題了哦~
  那么,它與腳本開頭的"# -*- coding:utf-8 -*-"有什么區別呢,注意,腳本開頭的編碼申明只是針對在腳本中輸入的非英文、數字、符號的字符串如中文,將其存成utf-8的形式,而非系統轉碼的形式。
  當開頭設置默認編碼時,很容易出現程序運行到setdefaultencoding就默認終止的情況(IDLE下),這時候需要在reload前后加入這個,目的是為了重新定向,防止reload將變量重置:

stdout = sys.stdout
reload(sys)
sys.stdout = stdout

IO時的編碼問題

  • txt
      1.寫txt文件時,windows下默認會寫出為ANSI編碼,在windows系統下就是GBK編碼。如果字符串被encode為utf-8,那么寫出的txt則為utf-8,但是當字符串為unicode時,如果有中文,寫出會報錯,原因就是按照系統默認編碼ascii進行編碼了,按之前所說的將默認編碼改為utf8就沒問題了,但輸出的也變成了utf-8。如果想寫入unicode,需要借助codecs庫的open方法,讀者可以自行百度。
      2.讀取txt文件時,如果txt文件為ANSI碼,則讀入的為gbk編碼,可用gbk解碼;如果txt文件為utf-8編碼,則讀入的也為utf-8編碼;而如果txt為unicode時,我們會發現一個奇怪的現象,讀入的文本編碼變成了"utf-16",所以需要用utf-16解碼(至於為什么留給讀者探索)。
  • csv
      寫出csv時,注意utf-8編碼和gbk編碼是不能用制表符\t進行分列的(excel顯示時),必須使用逗號,否則無法顯示分列結果。windows下推薦用gbk寫出,否則中文容易亂碼。當然,如果是包含大量文本的數據,非常不推薦使用csv輸出,一不小心就錯位,直接輸出excel是不錯的選擇。
  • excel
    xlrd,xlwt,xlsxwriter都是非常好的excel讀寫庫,xlrd目前支持讀寫xlsx(2007版)與xls(2003版),xlwt只支持寫出2003版xls,xlsxwriter支持2007版的寫出,而且輸入字符串均需要時unicode編碼才行,否則報錯。

網頁抓取時的編碼問題

  網頁抓取時遇到的主要問題,無非是網頁源代碼中摻雜了為被轉義的編碼形式,被作為純文本讀了進來,比如這樣一個字符串"\u6768\u777f",無論怎么print 它都是這個形式因為它是文本,不是編碼,那么怎么轉為中文呢,則需要用如下命令:

print text.decode('unicode_escape')

  非常生動形象的,這句話相當於是把“逃離”掉的unicode編碼進行再編碼,於是就得到了我們想要的中文。
  
  同樣,有的網頁中的文字是以反斜杠加三個數字形式呈現的,這個是標准的八進制字符串,如"\345\244\247",則表示一個中文字;而utf-8的表現形式為16進制字符串,像"\xe6\x9d\xa8"就代表着一個字 ,對於這些字符,只需要使用如下命令即可從文本轉為編碼字符串:

print text.decode('string_escape')

但是,python使用中還有諸多編碼問題,在此推薦我的好友何燕傑的腳本解釋型語言Cygnus,目前整個語言正處於開發與調試階段,完善后將會在博客園里給出。我為此開源項目貢獻了非常簡潔易用、強大的爬蟲庫(Requests),由於和c#完美對接,幾乎沒有任何編碼問題。大家敬請期待!

本博原創作品僅供品讀,歡迎評論,未經本人同意謝絕轉載。特此申明!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM