Python2 編碼問題分析

本文淺顯易懂,綠色純自然,手工製做,請放心閱讀。python

編碼問題是一個很大很雜的話題,要向完全的講明白能夠寫一本書了。導致亂碼的緣由不少,系統平臺、編程語言、多國語言、軟件程序支持、用戶選擇等均可能致使沒法正確的解析編碼。編程

致使亂碼的主要緣由能夠簡單歸結於文本的編碼方式和解碼方式不一樣致使的。本文將經過在win7(zh-cn)系統下分析python2.7的編解碼問題來簡單窺探一下編碼的冰山一角。網絡

從此遇到編碼問題時可以多一點分析解決思路,要是能起到一個拋磚引玉的做用,那就再好不過了。python2.7

1.爲何須要編碼

物理存儲介質上的基本存儲單元只能有兩種狀態,使用0和1區分。一段連續的0,1序列能夠表示特定的信息。編程語言

理論上任何字符均可以被惟一的一個連續0,1組成的bit序列表示。若是這樣的一個bit序列也惟一的表明一個字符,那麼咱們能夠由此創建一個bit序列和字符之間的轉換關係。學習

數字0 - 9和字母a - z(A - Z)等是人類可識別的字符,可是沒法存入到計算機的存儲介質中。編碼

十進制 二進制(bit 序列) 字符
 48   0011 0000  0
 65  0100 0001  A
 97  0110 0001  a

 

 

 

 

 

爲此,咱們將這些人類可識別的有意義的字符與特定0,1組成的bit序列創建起一一對應的關係。spa

由字符轉成對應的bit序列的過程稱爲編碼,將bit序列解釋成對應的字符則稱之爲解碼。插件

這樣的一個bit序列能夠用於存盤和網絡傳輸等全部只能使用二進制表示的環境中。當須要閱讀文件時再解碼並在顯示屏上顯示出人類可識別的字符。3d

編碼是人和機器之間的傳遞和表示信息的一種方式。

2.編碼方式

字符與bit序列之間的轉換隨之帶來的兩個問題是:

  1. 怎麼肯定每一個字符對應的編碼?
  2. 每一個字符的編碼長度應該是多少?

第一個問題能夠用一個你們公認的標準來解決。

第二個問題則主要是節約的角度考慮,在能夠表示特定字符集的狀況下,須要用盡量短的二進制序列。

問題看似簡單,但因爲歷史緣由,不一樣國家不一樣語種的編碼標準並不相同。並且即便同一個標準也在不斷的發展。

咱們常見的編碼標準ASCII,UTF-8,Unicode,GBK(中文編碼標準)等。

編碼長度由編碼方式決定,如ASCII碼錶示的字符都是一個字節(8 bits),Unicode編碼的字符通常用兩個字節。

同一種編碼中不一樣字符的編碼長度也可能不一樣。如UTF-8,對字符編碼儘可能壓縮以節儉空間,是一種「可變長編碼」。

既然存在這麼多種編碼方式,那麼對於一段通過編碼的二進制序列,若是以其餘的編碼方式解碼,顯然會獲得錯誤的解碼信息。

這就是咱們所遇到的亂碼問題。

那有沒有一種編碼方式能夠將世界上全部的可表示字符都賦予一個惟一的編碼?Unicode即是這樣一種編碼方式。

可是表示範圍一應俱全的一個代價就是字符編碼長度的增長。這樣數據傳輸和存放時佔用的網絡和空間資源就會更多。

UTF-8是在Unicode基礎上發展而來的可變長的編碼方式。

 Character    ASCII         Unicode  UTF-8 GBK
 0 00110000  00000000 00110000  00110000  00110000
 a 01100001  00000000 01100001  01100001  01100001
 字  沒法表示  01011011 01010111 (u'\u5b57') 11100101 10101101 10010111  ('\xe5\xad\x97')  11010111 11010110 ('\xd7\xd6')

 

 

 

 

 

 

 

 注意:

  上面的0是字符0,對應程序中「0」,而不是數字0。

  數字是不使用字符編碼的,數字可使用原碼、反碼和補碼錶示,在內存中通常使用補碼錶示,其字節序有大小端模式決定。

3.編碼轉換

不一樣的編碼有各自的特色,下面是一種可能的字符編輯顯示、加載傳輸和存儲對應的各階段編碼。

因爲Unicode能夠和任何編碼相互轉換,能夠藉助Unicode實現不一樣編碼之間的變換。

python2中有兩種表示字符串的類型:str 和 unicode。basestring是兩者的共同基類。

Unicode對象包含的是unicode字符,而str對象包含的是字節(byte)。

漢字「中」的編碼三種編碼方式

'\xd6\xd0'(GBK)  <==> u'\u4e2d'(UNICODE) <==> '\xe4\xb8\xad'(UTF-8)

在win7 + python2.7的環境下,Python 自帶的IDE中輸入的中文默認編碼方式爲GBK

字符串 '3132' 的編碼是 '\x31\x32\x33\x34'

 

對應第二節表中的數值能夠清楚的看到相同的字符對應的不一樣的編碼。

也能夠說明UTF-8和GBK的編碼兼容ASCII。而ASCII表示的字符在Unicode中則須要兩個字節表示。

一個以ASCII編碼的文檔(注意不是ANSI編碼)可使用UTF-8和GBK編碼方式打開,而不能使用Unicode。

爲了進一步驗證,新建notepad++文檔,選擇編碼方式爲ANSI(即GBK),用喜歡的輸入法以最快的方式鍵入漢字「中」。

使用HEX-Editor插件查看文檔的GBK編碼二進制表示:

 

「中」的UTF-8編碼二進制表示:

「中」的Unicode(對應notepad++中的USC-2 Big Endian)編碼二進制表示:

 

和python2中的結果相比能夠看出

 除了Unicode字符外,文件中存儲的二進制數據和對應的編碼是一一對應的。而這也驗證了先前說的Unicode對象自己包含的並非字節。

\u是unicode編碼的轉義字符,上面起始的0xfeff能夠看作unicode編碼文件的一個起始標記。

例如

Unicode:       前兩個字節爲FFFE;
Unicode big endian:  前兩字節爲FEFF; 

實際讀取文件時會根據文件的前兩個字節就能夠斷定出文件的具體格式。

使用gbk的方式讀取utf-8編碼文件(將utf-8文件編碼方式改成ANSI),將會以GBK的方式解讀字utf-8的節序列'\xe4\xb8\xad',結果以下:

反之,使用utf-8的方式讀取GBK編碼文件(以utf-8的方式解讀字GBK的節序列'\xd6\xd0'):

下面再看一個編解碼不一致致使的顯示問題:

看一下咱們IDLE默認編碼方式的確爲GBK方式

 下面是一個字符串,咱們平時常見的編碼錯誤大概就是這種形式。

'宸茬敤鏃墮棿'

這個能夠看作亂碼,由於組合沒有字面意思。其實這是一個utf-8編碼按照gbk方式解碼的結果,所以正確的方式先按照utf-8方式解碼,而後編碼成當前環境默認編碼方式而後輸出。

上面第二種錯誤的解碼致使了更深一層次的亂碼問題;解決這個問題是對字符串(非unicode)進行逆向的轉換

>>> '瀹歌尙鏁ら弮鍫曟?'.decode('utf-8').encode('gbk').decode('utf-8').encode('gbk')
'\xd2\xd1\xd3\xc3\xca\xb1\xbc\xe4'
>>> print _
已用時間

上面的轉換過程以下:

'\xe5\xae\xb8\xe8\x8c\xac\xe6\x95\xa4\xe9\x8f\x83\xe5\xa0\x95\xe6\xa3\xbf',    # 瀹歌尙鏁ら弮鍫曟?
'\xe5\xb7\xb2\xe7\x94\xa8\xe6\x97\xb6\xe9\x97\xb4',  #宸茬敤鏃墮棿
'\xd2\xd1\xd3\xc3\xca\xb1\xbc\xe4'  #已用時間

4.文件顯示標註編碼類型

在python文件中咱們可使用下面的方式標記文件的編碼類型:

# -*- coding:utf-8 -*-

#coding:utf-8

關於python文件編碼類型的聲明能夠參考官網: PEP 263

網上有不少人認爲上面的編碼聲明定義了python文件的編碼方式,但與其說定義python文件的編碼方式,不如說是這僅僅是對python解釋器的一種編碼方式聲明。即python 文件的編碼聲明 # -*- coding: utf-8 -*- 只是給python解釋器看的。當python解釋器執行py文件時會根據這個聲明來解析文件編碼格式。

也就是說這個聲明和文件自己實際的文件編碼沒有關係。但二者最好保持一致,否者python解釋器會以錯誤的方式去解碼文件內容並執行。

 

 上面的文件聲明 # -*- coding:gbk -*- 誤導python解釋器以gbk的編碼方式去解析一個utf-8編碼的文件。

實際開發過程當中只須要統一將文件聲明爲utf-8便可。

再解釋一下文件相關的編碼概念:

文件自己的編碼方式,該編碼決定了文件數據以怎樣的二進制格式保存在存儲介質中。

文件內容顯示的格式(解碼方式),該編碼方決定了文件內容是以怎樣的解碼方式被顯示出來的。

通常文本顯示類的程序打開一個文本時會根據文本文件起始字節判斷該文本的編碼方式,固然也能夠有用戶手動改變文件的解碼方式。

若是文本程序或用戶選擇和編碼方式不兼容的錯誤的解碼方式,可能就會出現亂碼。

可是對於可執行py文件而言,python解釋器會以py文件開頭的編碼聲明方式來解釋文件內容,若是沒有聲明,python解釋器會以默認的ASCII編碼解析文件。

也就是隻要python可執行文件的聲明編碼只要和實際文件的編碼方式一致,就沒有任何問題。所以可使用# -*- coding:gbk -*-做爲聲明,但考慮到兼容性和可移植性,最好編碼和聲明均採用utf-8。

以上結果均在特定的平臺和環境下得出,僅爲我的看法。若有錯誤歡迎指正,共同探討學習。

相關文章
相關標籤/搜索