1. 程式人生 > >Python:字元編碼詳解

Python:字元編碼詳解

相關文章

1. 字元編碼簡介

1.1. ASCII

ASCII(American Standard Code for Information Interchange),是一種單位元組的編碼。計算機世界裡一開始只有英文,而單位元組可以表示256個不同的字元,可以表示所有的英文字元和許多的控制符號。不過ASCII只用到了其中的一半(\x80以下),這也是MBCS得以實現的基礎。

1.2. MBCS

然而計算機世界裡很快就有了其他語言,單位元組的ASCII已無法滿足需求。後來每個語言就制定了一套自己的編碼,由於單位元組能表示的字元太少,而且同時也需要與ASCII編碼保持相容,所以這些編碼紛紛使用了多位元組來表示字元,如GBxxx

BIGxxx等等,他們的規則是,如果第一個位元組是\x80以下,則仍然表示ASCII字元;而如果是\x80以上,則跟下一個位元組一起(共兩個位元組)表示一個字元,然後跳過下一個位元組,繼續往下判斷。

這裡,IBM發明了一個叫Code Page的概念,將這些編碼都收入囊中並分配頁碼,GBK是第936頁,也就是CP936。所以,也可以使用CP936表示GBK。

MBCS(Multi-Byte Character Set)是這些編碼的統稱。目前為止大家都是用了雙位元組,所以有時候也叫做DBCS(Double-Byte Character Set)。必須明確的是,MBCS並不是某一種特定的編碼,Windows里根據你設定的區域不同,MBCS指代不同的編碼,而Linux裡無法使用MBCS作為編碼。在Windows中你看不到MBCS這幾個字元,因為微軟為了更加洋氣,使用了ANSI

來嚇唬人,記事本的另存為對話方塊裡編碼ANSI就是MBCS。同時,在簡體中文Windows預設的區域設定裡,指代GBK。

1.3. Unicode

後來,有人開始覺得太多編碼導致世界變得過於複雜了,讓人腦袋疼,於是大家坐在一起拍腦袋想出來一個方法:所有語言的字元都用同一種字符集來表示,這就是Unicode。

最初的Unicode標準UCS-2使用兩個位元組表示一個字元,所以你常常可以聽到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的區別後,這兩個方法就不會再混淆了:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 # 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字元,則需要在檔案頭部進行字元編碼的宣告,如下:

?
1 #-*- 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將先使用原始碼檔案宣告的字元編碼進行編碼然後寫入。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # coding: UTF-8 f = open('test.txt') s = f.read() f.close() print type(s) # <type 'str'> # 已知是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()來說,這個方法比較不容易在編碼上出現問題。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # coding: GBK import codecs f = codecs.open('test.txt', encoding='UTF-8') u = f.read() f.close() print type(u) # <type 'unicode'> 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模組中提供了一些獲取當前環境下的預設編碼的方法。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # 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小節中的程式碼的結果:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #中文(簡體, 中國) #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用的相當少,但用到的時候還是需要注意。

--END--

相關推薦

Python字元編碼

相關文章 1. 字元編碼簡介 1.1. ASCII ASCII(American Standard Code for Information Interchange),是一種單位元組的編碼。計算機世界裡一開始只有英文,而單位元組可以表示256個不同的字元,可以表示所有的英文字元和許多的控制符號。不過

python__getitem__方法

__getitem__(self,key): 這個方法返回與指定鍵想關聯的值。對序列來說,鍵應該是0~n-1的整數,其中n為序列的長度。對對映來說,鍵可以是任何型別。 class Tag: def __init__(self,id): self.id=id

Python2.7字元編碼

Python2.7字元編碼詳解 Python27字元編碼詳解 宣告 一 字元編碼基礎 1 抽象字元清單ACR 2 已編碼字符集CCS 3 字元編碼格式CEF

前端開發中的字元編碼

前端開發過程中會接觸各種各樣的編碼,比較常見的主要是UTF-8和HTML實體編碼,但是web前端的世界卻不止這兩種編碼,而且編碼的選擇也會造成一定的問題,如前後端開發過程中不同編碼的相容、多位元組編碼可能會造成的XSS漏洞等。因此,本文旨在更好的全面瞭解涉及前端開發領域的

各種字元編碼(ascii,utf-8,unicode,gbk,gb2312,iso8859-1等)

1. ASCII 我們需要了解的最早編碼是ASCII碼。它用7個二進位制位來表示,由於那個時期生產的大多數計算機使用8位大小的位元組,因此使用者不僅可以存放所有可能的ASCII字元,而且有整整一位空餘下來。如果你技藝高超,可以將該位用做自己離奇的目的:WordStar中那

字元編碼——徹底理解掌握編碼知識,“亂碼”不復存在

每一個程式設計師都不可避免的遇到字元編碼的問題,特別是做Web開發的程式設計師,“亂碼問題”一直是讓人頭疼的問題,也許您已經很少遇到“亂碼”問題,然而,對解決亂碼的方法的內在原理,您是否明白?本人作為一個程式設計師,在字元編碼方面同樣遇到不少問題,而且一直對各種編碼懵懵懂懂、不清不楚;在工作中也曾經遇到一

Java字元編碼

char “位元組”是byte,“位”是bit ; 1 byte = 8 bit ; char 在Java中是2個位元組。java採用unicode(《java核心技術卷一》裡面有詳細說明),2個位元組(16位)來表示一個字元。而Unico

計算機字元編碼——從理論到實踐

前言 最近在看《深入理解計算機系統》,讀到“字元編碼”時不禁想起了初學時那段痛苦的歲月,同時又沒找到一篇將理論和實踐結合在一起的文章,為此決定自己寫一份。希望能把我走過的彎路總結出來,能幫助一些還在路上的朋友。 關於計算機如何儲存資訊,請參考《深入理解計

計算機字元編碼——從理論到實踐——參考資料

gbk與gb2312的區別: GB2312是中國規定的漢字編碼,也可以說是簡體中文的字符集編碼; GBK 是 GB2312的擴充套件 ,除了相容GB2312外,它還能顯示繁體中文,還有日文的假名。 總體說來,GBK包括所有的漢字,包括簡體和繁體。而gb2

python正則表示式(二)特殊字元序列

內容提要: 說明:僅供學習交流使用 二、python正則表示式中的特殊字元序列 \number   \A  \Z   \b    \B    \d  \D   \s  \S  \w   \W      \\ 2.1\number  以相同的序號代表的組所匹配的內容

Python學習一序列基礎

list ava author 萬裏 單個 使用下標 不能 get 分別是 作者:NiceCui 本文謝絕轉載,如需轉載需征得作者本人同意,謝謝。 本文鏈接:http://www.cnblogs.com/NiceCui/p/7858473.html 郵箱:moyi

Python第二天 變量及變量賦值

number ont 時間 目錄 命令 code toc 編號 運用 目錄 [toc] 此文章針對剛學Python的小白,若覺得對變量有很好的掌握,可以觀看其他的文章在這裏, 我說一下我對變量的簡單總結: 變量是為了存儲運算程序中的一些中間結果, 為了方便日後調用 什麽

python爬蟲學習筆記二Requests庫及HTTP協議

Requests庫的安裝:https://mp.csdn.net/postedit/83715574 r=requests.get(url,params=None,**kwargs) 這個r是Response物件 url :擬獲取頁面的url連結 params:url中的額外引數

python 學習彙總27itertools函式( tcy)

itertools函式 2018/11/14 2.1.建立新iter: count(start=0, step=1)#無限迴圈數;按Ctrl + C退出 # 返回均勻間隔值無限流;通常用作map()生成連續資料點的引數。此外,用於zip()新增序列號 g = itertools.count

Python學習【第5篇】Python字元編碼問題 python之----------字元編碼具體原理

python之----------字元編碼具體原理 1.記憶體和硬碟都是用來儲存的。 CPU:速度快 硬碟:永久儲存 &nb

廖雪峰老師Python學習(2)字元編碼

字元編碼 我們已經講過了,字串也是一種資料型別,但是,字串比較特殊的是還有一個編碼問題。 因為計算機只能處理數字,如果要處理文字,就必須先把文字轉換為數字才能處理。最早的計算機在設計時採用8個位元(bit)作為一個位元組(byte),所以,一個位元組能表示的最大的整數就是255(二進位制11

資料結構圖文解析之哈夫曼樹與哈夫曼編碼及C++模板實現

0. 資料結構圖文解析系列 1. 哈夫曼編碼簡介 哈夫曼編碼(Huffman Coding)是一種編碼方式,也稱為“赫夫曼編碼”,是David A. Huffman1952年發明的一種構建極小多餘編碼的方法。 在計算機資料處理中,霍夫曼編碼使用變長編碼表對源符號進行編碼,出現頻率較高的源符號採用較短的編碼,

Python筆記字元編碼unicode/utf-8

請尊重原創作品。轉載請保持文章完整性,並以超連結形式註明原始作者“tingsking18”和主站點地址,方便其他朋友提問和指正。 Unicode和Python的中文處理   在Python語言中,Uincode字串處理一直是一個容易讓人迷惑的問題。許多Python

Python __init__.py 作用

引用文件 site linu 塊對象 and 語句 inux python url __init__.py 文件的作用是將文件夾變為一個Python模塊,Python 中的每個模塊的包中,都有__init__.py 文件。 通常__init__.py 文件為空,但是我們還可

Linuxat命令

計時 osi 執行 inux days pan 必須 man 一個 at命令 at命令為單一工作調度命令。at命令非常簡單,但是在指定時間上卻非常強大 語法 at [選項] time at > 執行的命令 ctrl+d 選項 -m :當指定的任務被