Python字符編碼詳細介紹

高洛峰
發布: 2017-03-28 17:19:58
原創
1423 人瀏覽過

1. 字元編碼簡介

1.1. ASCII

ASCII(American Standard Code for Information Interchange),是一種單字節的編碼。表示所有的英文字元和許多的控制符號。世界很快就有了其他語言,單字節的ASCII已無法滿足需求。相容,所以這些編碼紛紛使用了多字節來表示字符,如GBxxx、BIGxxx等等,他們的規則是,如果第一個字節是\x80以下,則仍然表示ASCII字符;而如果是\x80以上,則跟下一個位元組一起(共兩個位元組)表示一個字符,然後跳過下一個位元組,繼續往下判斷。將這些編碼都收入囊中並分配頁碼,GBK是第936頁,也就是CP936。是這些編碼的統稱。設定的區域不同,MBCS指涉不同的編碼,而

Linux

裡無法使用MBCS作為編碼。來嚇唬人,記事本的另存為對話框裡編碼ANSI就是MBCS。 ,有人開始覺得太多編碼導致世界變得過於複雜了,讓人腦袋疼,於是大家坐在一起拍腦袋想出來一個方法:所有語言的字符都用同一種

字符集

來表示,這就是Unicode。但過了不久有人覺得256*256太少了,還是不夠用,於是出現了UCS-4標準,它使用4個位元組表示一個字符,不過我們用的最多的仍然是UCS-2。

UCS(Unicode Character Set)還只是字元對應碼位元的一張表而已,例如"漢"這個字的碼位是6C49。字符具體如何傳輸和儲存則是由UTF(UCS Transformation Format)來負責。

一開始這事很簡單,直接使用UCS的碼位來保存,這就是UTF-16,比如,"漢"直接使用\x6C\x49保存(UTF-16-BE),或倒過來使用\x49\x6C儲存(UTF-16-LE)。但用著用美國人覺得自己吃了大虧,以前英文字母只需要一個字節就能保存了,現在大鍋飯一吃變成了兩個字節,空間消耗大了一倍……於是UTF-8橫空出世。 UTF-8是一種很彆扭的編碼,具體表現在他是變長的,並且相容於ASCII,ASCII字元使用1位元組表示。然而這裡省了的必定是從別的地方摳出來的,你一定也聽過UTF-8里中文字符使用3個位元組來保存吧? 4個位元組保存的字元更是在淚奔…(具體UCS-2是怎麼變成UTF-8的請自行搜尋)另外值得一提的是BOM(Byte Order Mark)。我們在儲存文件時,文件使用的編碼並沒有保存,打開時則需要我們記住原先保存時使用的編碼並使用這個編碼打開,這樣一來就產生了許多麻煩。 (你可能想說記事本打開文件時並沒有讓選編碼?不妨先打開記事本再使用文件-> 打開看看)而UTF則引入了BOM來表示自身編碼,如果一開始讀入的幾個位元組是其中之一,則代表接下來要讀取的文字使用的編碼是對應的編碼:

BOM_UTF8 '\xef\xbb\xbf' 

BOM_UTF16_LE '\xff\xfe' 

BOM_UTF16_BE '\xfe\xff'並不是所有的

編輯器

都會寫入BOM,但即使沒有BOM,Unicode還是可以讀取的,只是像MBCS的編碼一樣,需要另行指定具體的編碼,否則解碼將會失敗。

你可能聽過UTF-8不需要BOM,這種說法是不對的,只是絕大多數編輯器在沒有BOM時都是以UTF-8作為預設編碼讀取。即使是儲存時預設使用ANSI(MBCS)的記事本,在讀取檔案時也是先使用UTF-8測試編碼,如果可以成功解碼,則使用UTF-8解碼。記事本這個彆扭的做法造成了一個BUG:如果你新建文字檔並輸入"奼塧"然後使用ANSI(MBCS)保存,再打開就會變成"漢a",你不妨試試:)

2. Python2.x中的編碼問題

2.1. str和unicode

str和unicode都是basestring的子類別。嚴格意義上說,str其實是位元組串,它是unicode經過編碼後的位元組組成的序列。對UTF-8編碼的str'漢'使用len()函數時,結果是3,因為實際上,UTF-8編碼的'漢' == '\xE6\xB1\x89'。

unicode才是真正意義上的字串,對字節串str使用正確的字元編碼進行解碼後獲得,並且len(u'漢') == 1。

再來看看encode()和decode()兩個basestring的實例方法,了解str和unicode的差異後,這兩個方法就不會再混淆了:

# coding: UTF-8
 
u = u'汉'
print repr(u) # u'\u6c49'
s = u.encode('UTF-8')
print repr(s) # '\xe6\xb1\x89'
u2 = s.decode('UTF-8')
print repr(u2) # u'\u6c49'
 
# 对unicode进行解码是错误的
# s2 = u.decode('UTF-8')
# 同样,对str进行编码也是错误的
# u2 = s.encode('UTF-8')
登入後複製

需要注意的是,雖然對str呼叫encode()方法是錯誤的,但實際上Python不會拋出異常,而是傳回另外一個相同內容但不同id的str;對unicode呼叫decode ()方法也是這樣。很不懂為什麼不把encode()和decode()分別放在unicode和str中而是都放在basestring中,但既然已經這樣了,我們就小心避免犯錯吧。

2.2. 字符編碼聲明

源代碼文件中,如果有用到非ASCII字符,則需要在文件頭部進行字符編碼的聲明,如下:

#-*- coding: UTF-8 -*-
登入後複製

實際上Python只檢查#、coding和編碼字串,其他的字元都是為了美觀加上去的。另外,Python中可用的字元編碼有很多,而且還有許多別名,還不區分大小寫,例如UTF-8可以寫成u8。參見http://docs.python.org/library/codecs.html#standard-encodings。

另外要注意的是宣告的編碼必須與檔案實際保存時用的編碼一致,否則很大幾率會出現程式碼解析例外。現在的IDE一般會自動處理這種情況,改變聲明後同時換成聲明的編碼保存,但文本編輯器控們需要小心:)

2.3. 讀寫文件

內置的open()方法開啟檔案時,read()讀取的是str,讀取後需要使用正確的編碼格式進行decode()。 write()寫入時,如果參數是unicode,則需要使用你希望寫入的編碼進行encode(),如果是其他編碼格式的str,則需要先用該str的編碼進行decode(),轉成unicode後來再使用寫入的編碼進行encode()。如果直接將unicode作為參數傳入write()方法,Python將先使用原始碼檔案聲明的字元編碼進行編碼然後寫入。

# coding: UTF-8
 
f = open('test.txt')
s = f.read()
f.close()
print type(s) # <type &#39;str&#39;>
# 已知是GBK编码,解码成unicode
u = s.decode('GBK')
 
f = open('test.txt', 'w')
# 编码成UTF-8编码的str
s = u.encode('UTF-8')
f.write(s)
f.close()
登入後複製

另外,模組codecs提供了一個open()方法,可以指定一個編碼打開文件,使用這個方法打開的文件讀取返回的將是unicode。寫入時,如果參數是unicode,則使用open()時指定的編碼進行編碼後寫入;如果是str,則先根據原始程式碼檔案聲明的字元編碼,解碼成unicode後再進行前述操作。相對內建的open()來說,這個方法比較不容易在程式碼上出現問題。

# coding: GBK
 
import codecs
 
f = codecs.open('test.txt', encoding='UTF-8')
u = f.read()
f.close()
print type(u) # <type &#39;unicode&#39;>
 
f = codecs.open('test.txt', 'a', encoding='UTF-8')
# 写入unicode
f.write(u)
 
# 写入str,自动进行解码编码操作
# GBK编码的str
s = '汉'
print repr(s) # '\xba\xba'
# 这里会先将GBK编码的str解码为unicode再编码为UTF-8写入
f.write(s)
f.close()
登入後複製

2.4. 與編碼相關的方法

sys/locale模組中提供了一些取得目前環境下的預設編碼的方法。

# coding:gbk
 
import sys
import locale
 
def p(f):
    print '%s.%s(): %s' % (f.module, f.name, f())
 
# 返回当前系统所使用的默认字符编码
p(sys.getdefaultencoding)
 
# 返回用于转换Unicode文件名至系统文件名所使用的编码
p(sys.getfilesystemencoding)
 
# 获取默认的区域设置并返回元祖(语言, 编码)
p(locale.getdefaultlocale)
 
# 返回用户设定的文本数据编码
# 文档提到this function only returns a guess
p(locale.getpreferredencoding)
 
# \xba\xba是'汉'的GBK编码
# mbcs是不推荐使用的编码,这里仅作测试表明为什么不应该用
print r"'\xba\xba'.decode('mbcs'):", repr('\xba\xba'.decode('mbcs'))
 
#在笔者的Windows上的结果(区域设置为中文(简体, 中国))
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'
登入後複製

3.一些建議

3.1. 使用字元編碼聲明,並且同一工程中的所有原始程式碼檔案使用相同的字元編碼聲明。

這一點是一定要做到的。

3.2. 拋棄str,全部使用unicode。

按引號前先按一下u最初做起來確實很不習慣而且常常會忘記再跑回去補,但如果這麼做可以減少90%的編碼問題。如果編碼困擾不嚴重,可以不參考此條。

3.3. 使用codecs.open()取代內建的open()。

如果編碼困擾不嚴重,可以不參考此條。

3.4. 絕對需要避免使用的字元編碼:MBCS/DBCS和UTF-16。

這裡說的MBCS不是指GBK什麼的都不能用,而是不要使用Python裡名為'MBCS'的編碼,除非程式完全不移植。

Python中編碼'MBCS'與'DBCS'是同義詞,指目前Windows環境中MBCS指涉的編碼。 Linux的Python實作中沒有這種編碼,所以一旦移植到Linux一定會出現異常!另外,只要設定的Windows系統區域不同,MBCS指涉的編碼也是不一樣的。分別設定不同的區域運行2.4小節的程式碼的結果:

#中文(简体, 中国)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'
 
#英语(美国)
#sys.getdefaultencoding(): UTF-8
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
 
#德语(德国)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
 
#日语(日本)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp932')
#locale.getpreferredencoding(): cp932
#'\xba\xba'.decode('mbcs'): u'\uff7a\uff7a'
登入後複製

可见,更改区域后,使用mbcs解码得到了不正确的结果,所以,当我们需要使用'GBK'时,应该直接写'GBK',不要写成'MBCS'。

UTF-16同理,虽然绝大多数操作系统中'UTF-16'是'UTF-16-LE'的同义词,但直接写'UTF-16-LE'只是多写3个字符而已,而万一某个操作系统中'UTF-16'变成了'UTF-16-BE'的同义词,就会有错误的结果。实际上,UTF-16用的相当少,但用到的时候还是需要注意。

以上是Python字符編碼詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!