python中的編碼模式

 

一、編碼系統的出現和發展

PC剛開始出現時,只有ASCII一種編碼系統,因爲這種編碼系統只包括大小寫的英文字母、數字、控制字符等127個字符,所以對英語用戶是友好的。

隨着PC在全球的日益普及,各個國家也需要對本國的語言字符進行編碼,以方便對包含本國語言的信息進行處理。這其中,大陸出現了gb2312等編碼系統,臺灣,韓國,日本也出現了自己的編碼系統。這些編碼系統出現的時間比ASCII晚,爲了兼容ASCII碼,這些編碼系統都在ASCII碼基礎上做了擴展,但是每個編碼系統都有自己的數字和字符的映射方式,造成了這些編碼系統之間的不兼容性,因此以某種編碼系統創建的字符串,如果用另一種編碼系統進行解碼的話,就會出現亂碼。

爲了解決編碼系統混亂的局面,Unicode編碼系統出現了,它將世界上所有的語言字符和符號都進行統一的編碼。既然大家都採用同一種編碼系統,自然也就不會混亂了。

 

 

二、字節串和字符串的區別:

字節串,顧名思義,就是一個字節的序列,這也是PC中數據的最終格式(傳輸或存儲)。字符在計算機中只是一種抽象,字符串是這種抽象的序列。

例如,“你好”這兩個漢字,呈現在你面前的就是2個字符長度的字符串,如果採用gb2312編碼系統進行保存的話,則是一個4個字節長度的字節串,而採用utf-8編碼進行保存,就是一個6個字節長度的字節串。剛開始瞭解字符編碼的概念時,可能會將字節串和字符串混爲一談,明確分清這兩個概念後,會對後面的編碼系統有更好的理解。

 

 

三、介紹幾種常見的編碼

ASCII編碼系統,

ASCII是American Standard Code for Information Interchange的縮寫,每個ASCII碼以1個字節(Byte)儲存,從0到數字127代表不同的常用符號,例如大寫A的ASCII碼是65,小寫a則是97。由於ASCII只佔用一個字節的低七個位,最高位並不使用,所以最高位保持爲0

 

gb2312, gbk

GB2312(1980年)一共收錄了7445個字符,由於支持的漢字太少。1995年的漢字擴展規範GBK1.0收錄了21886個符號。2000年的 GB18030是取代GBK1.0的正式國家標準。該標準收錄了27484個漢字,同時還收錄了藏文、蒙文、維吾爾文等主要的少數民族文字。

從ASCII、GB2312、GBK 到GB18030,這些編碼方法是向下兼容的,即同一個字符在這些方案中總是有相同的編碼,後面的標準支持更多的字符。在這些編碼中,英文和中文可以統一地處理。ascii字符還是以一個字節存儲,而非ascii字符(如漢字)使用2個字節存儲。區分中文編碼的方法是高字節的最高位爲1。GB2312、GBK和GB18030都屬於雙字節字符集 (DBCS)。在讀取DBCS字節流時,只要遇到高位爲1的字節,就可以將該字節和下一個字節作爲一個雙字節編碼,而不用管低字節的高位是什麼。

 

Unicode

在網上關於Unicode的資料有很多,比如http://zh.wikipedia.org/wiki/Unicode,就不過多描述了。

 

 

四、Unicode和其他編碼系統的區別

概念上,Unicode編碼系統可分爲編碼方式和實現方式兩個層次。目前實際應用的編碼方式是版本UCS-2,即採用16位的編碼空間,也就是每個字符佔用2個字節;而實現方式採用的是utf-8等編碼。其他編碼系統如ASCIIgbk等並不區分編碼方式和實現方式。

 

爲什麼會出現這種差別?

我認爲2個方面的原因導致了這種差別:

1 佔用空間的大小   如果Unicode和其他編碼系統一樣,不區分編碼方式和實現方式,統一用2個字節表示和保存信息,那麼存儲和傳輸全部由ASCII字符組成的信息,將要比其他編碼系統浪費一倍的空間和時間。如果採用4個字節表示一個字符的UCS-4標準,浪費的空間會更大。而在這方面,其他編碼系統沒有這種缺點,因爲這些編碼系統採用中英文共存的方式進行編碼,在這些編碼系統中,ASCII字符還是佔用一個字節,而非ASCII字符佔用2個字節。

2 編碼方式的識別   目前的xmlhtml和其他一些文檔都會在文件的開頭用ASCII字符標識出編碼方式,因此軟件在識別此類文件時,會先讀取文件頭的部分數據(字節串),以ASCII編碼方式進行解碼(轉成字符串,因爲ASCII編碼中字節串和字符串完全相同,所以這部分數據可以直接當成字符進行處理),進而找到文檔的編碼方式,最後以找到的編碼方式再對數據進行完整的解碼操作。如果按照2個字節代表一個字符存儲,這種自標識編碼系統的方式就無法使用了。

基於以上2個原因,Unicode編碼系統分爲2個層次,在內存操作字符串時,使用2個字節表達一個字符的編碼方式,但是在存儲和傳輸時,使用utf-8等實現方式的編碼。utf-8編碼對ASCII字符的編碼和其他編碼系統一樣,只佔用一個字節,而對於非ASCII字符則需要多個(>=2)字節。

 

我們再往深處想一想,按照Unicode編碼系統的2個層次的劃分,不但utf-8編碼可以作爲Unicode字符的存儲編碼方案,其它如gbkbig5等編碼都可以用做Unicode字符的存儲編碼方案,只不過使用這些編碼系統保存Unicode字符時,只能保存該編碼系統支持的字符集和Unicode字符集的交集部分,不像utf-8存儲編碼方案那樣,和Unicode字符有直接的映射關係,可以保存所有的Unicode字符。

但是utf-8編碼方案也有自己的缺點,雖然在保存ASCII字符方面和其他編碼系統在佔用空間上一樣,但是對於非ASCII字符,卻比其他編碼方案要多,例如對簡體漢字的“漢”字,gbk只佔用2個字節,utf-8需要使用3個字節來保存。

因此目前在Windows系統內部使用Unicode編碼處理字符串,但是在存儲上還是採用默認的本地編碼方式。比如我使用的WindowsXP簡體中文版,記事本,word等程序默認使用gb2312編碼。而常用的notepad++編輯器,默認的也是ansi(即gb2312編碼),但是也提供了utf-8格式用於保存文本。

 

 

五、數據編碼方式的確定

當用戶接收到代表信息的數據(字節串)時,如何確定數據的編碼方式呢?

當前瞭解到的有3種方式:

檢測,依照某個算法對字節串檢索,當其符合某個編碼系統的特徵時,就以該編碼系統對字節串解碼,但這種方式的準確率不高,經常會出現亂碼的現象;指定,其中又細分爲2類:一類是上面描述過的通過在文件開始處以ASCII碼指定編碼方式,另一類是用戶瞭解數據的編碼方式,由用戶指定應該以何種編碼系統對系統進行解碼,常見的例子有:瀏覽器中用戶可以指定網頁的解碼方式,一些編輯器如notepad++也可以讓用戶指定文件內容的編碼方式;

 

 

六、python處理字符的方式

python對字符串的處理分爲三個階段:

python2.2之前,沒有對unicode的支持,只有str數據類型;2.2版開始,添加了unicode數據類型,strunicode數據類型共存;python重大的改進版本3.x中,str迴歸了該數據類型的本意,只表示字符串,和2.x版本中的unicode數據類型等同;而字節串由兩種數據類型表示,bytes表示不可改變的字節串,bytearray表示可以改變內容的字節串;

 

http://www.woodpecker.org.cn/diveintopython3/strings.html 中詳細解釋了python3中對字符處理的細節,也包括對編碼系統原理和演化過程極爲精彩的描述,這裏就不詳述了。因爲python2.x版本還在大量應用中,因此這裏着重講解python2.x對字符串的處理方式。

 

python2.x中常用的字符串類型有兩種,一種是str,另一種是unicode。實際上str擔當着2個數據類型的作用:字節串ascii字符串。想一想從文件讀出的數據用什麼類型表示,你就能理解我的意思了。如果只是處理ASCII碼的字符串操作,str足矣;但是一旦碰上非ASCII字符串,str就無能爲力了。

舉個例子來說明這個問題:在字符串“我 love 中文”(採用gb2312編碼12bytesutf-8編碼爲15bytes)中查找字符串“ov”,這相當於在一個字節串中查找一個2bytes序列,其中第一個字節值爲82,第二個字節值爲89,即使這個字節串中包含非ASCII字符,因爲採用值相等的查找方式,str.find()函數是可以勝任的。

但如果是str.find("中文"),可就麻煩了,如果源字符串和查找子串採用相同的編碼方式,str.find()還可以正常工作,但如果採用了不同的編碼方式,就得不到正確的結果了,而且即使採用同一種編碼方式,找到了匹配的子串開始位置,但這個位置是以字節作爲單位的,並不是以字符爲單位的,如果你想作進一步的處理,還要參考這個字符串的編碼方式纔行。

所以在python2.x中,使用unicode數據類型纔是處理字符串的正確方式,在處理任何字符串之前,先將輸入的字節串(str)按照指定的編碼方式轉換成字符串(unicode),然後進行處理,處理的結果按照你想要的編碼方式再轉換成字節串(str)以保存或傳輸。

簡單來說,在python2.x中字符串的處理方式就是:解碼(decode->處理->編碼(encode),解碼按照指定的編碼方式將字節流轉換成字符流,編碼按照指定的編碼方式將字符流轉換成字節流python提供了encodedecode兩個函數用於字節串和字符串間的轉換。

 

 

七、幾個涉及字符編碼的典型場景

1  python源代碼

如果不指定python代碼的編碼方式,python2.x默認是按照ascii來解釋python源文件的,如果源代碼中包括非ascii字符,需要在文件開始處指出python的編碼方式。唯一的例外是如果python代碼是以utf-8等編碼方式保存的,也可以不聲明編碼方式,具體原因和實現細節有關。

python編碼方式的聲明和以utf-8編碼保存而無需指定編碼方式的細節可以參看PEP-0263http://www.python.org/dev/peps/pep-0263/

 

2 輸入輸出

來自於console,文件,網絡的輸入都是基於字節的,在進行字符串處理之前,需要按照指定的編碼方式進行解碼。比如,來自新浪的一個網頁:http://news.sina.com.cn 在這個web page開頭聲明瞭編碼方式爲gb2312,再繼續處理之前,先使用decode函數解碼爲unicode字符串:web_page.decode('gb2312')

當經過處理的字符串需要保存時,需要按照指定的編碼方式進行編碼,再保存到文件中,如將一個xml文檔轉換爲文件數據保存:fd.write(unicode_xml.encode('utf-8'))

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章