(轉載)Python常見字符編碼間的轉換

Python常見字符編碼間的轉換

主要內容:

    1、Unicode 和 UTF-8的愛恨糾葛

    2、字符在硬盤上的存儲

    3、編碼的轉換

    4、驗證編碼是否轉換正確

    5、Python bytes類型

 

  前言:

  學習Python,字符編碼間的轉換是繞不過去的一隻攔路虎,不把編碼徹底搞明白,總有一天它會猝不及防坑你一把。

  Python2.x和Python3.x在字符編碼的設置上也有很大區別(Python3未來將是主流,所以Python3爲主),今天我們就來一起學習下。

  上一篇文章裏我已經簡述了Python的常見編碼了,這裏就不再贅述了,還不清楚的小夥伴可以先去看下:  http://www.cnblogs.com/schut/p/8406897.html

 

一、Unicode 和 UTF-8的愛恨糾葛  

  Unicode 起到了2個作用:

  1. 直接支持全球所有語言,每個國家都可以不用再使用自己之前的舊編碼了,用unicode就可以了。(就跟英語是全球統一語言一樣)
  2. unicode包含了跟全球所有國家編碼的映射關係。

    Unicode解決了字符和二進制的對應關係,但是使用unicode表示一個字符,太浪費空間。

    例如:利用unicode表示“Python”需要12個字節才能表示,比原來ASCII表示增加了1倍。

    由於計算機的內存比較大,並且字符串在內容中表示時也不會特別大,所以內容可以使用unicode來處理,但是存儲和網絡傳輸時一般數據都會非常多,那麼增加1倍將是無法容忍的!!!

  爲了解決存儲和網絡傳輸的問題,出現了Unicode Transformation Format,學術名UTF,即:對unicode中的進行轉換,以便於在存儲和網絡傳輸時可以節省空間!

  UTF-8: 使用1、2、3、4個字節表示所有字符;優先使用1個字符、無法滿足則使增加一個字節,最多4個字節。英文佔1個字節、歐洲語系佔2個、東亞語系佔3個,其它及特殊字符佔4個。

  UTF-16: 使用2、4個字節表示所有字符;優先使用2個字節,否則使用4個字節表示。

  UTF-32: 使用4個字節表示所有字符。

  總結:UTF 是爲unicode編碼 設計 的一種在存儲和傳輸時節省空間的編碼方案。



二、字符在硬盤上的存儲

  首先要明確的一點就是,無論以什麼編碼在內存裏顯示字符,存到硬盤上都是2進制(0b是說明這段數字是二進制,0x表示是16進制。0x幾乎所有的編譯器都支持,而支持0b的並不多)。理解這一點很重要。

  比如:        

 

ascii編碼(美國):
        l   0b1101100
        o   0b1101111
        v   0b1110110
        e   0b1100101
GBK編碼(中國):
        老   0b11000000 0b11001111
        男   0b11000100 0b11010000
        孩   0b10111010 0b10100010

複製代碼

  還要注意的一點是:存到硬盤上時是以何種編碼存的,再從硬盤上讀出來時,就必須以何種編碼讀(開頭聲明或轉換),要不然就亂了。

三、編碼的轉換
  雖然有了unicode and utf-8 ,但是由於歷史問題,各個國家依然在大量使用自己的編碼,
比如中國的windows,默認編碼依然是gbk,而不是utf-8。
  基於此,如果中國的軟件出口到美國,在美國人的電腦上就會顯示亂碼,因爲他們沒有gbk編碼。
  所以該怎麼辦呢?
  還記得我們講unicode其中一個功能是其包含了跟全球所有國家編碼的映射關係,這時就派上用場了。
無論你以什麼編碼存儲的數據,只要你的軟件在把數據從硬盤讀到內存裏,轉成unicode來顯示,就可以了。
由於所有的系統、編程語言都默認支持unicode,那你的gbk軟件放到美國電腦上,加載到內存裏,變成了unicode,
中文就可以正常展示啦。
 
 Python3執行過程
1、解釋器找到代碼文件,把代碼字符串按文件頭定義的編碼加載到內存,轉成unicode
2、把代碼字符串按照語法規則進行解釋
3、所有的變量字符都會以unicode編碼聲明
  在py3上 把你的代碼以utf-8編寫, 保存,然後在windows上執行。
  發現可以正常執行!
  其實utf-8編碼之所以能在windows gbk的終端下顯示正常,是因爲到了內存裏python解釋器把utf-8轉成了unicode , 
但是這只是python3, 並不是所有的編程語言在內存裏默認編碼都是unicode,比如 萬惡的python2 就不是,
它是ASCII(龜叔當初設計Python時的一點缺陷),想寫中文,就必須聲明文件頭的coding爲gbk or utf-8, 聲明之後,python2解釋器
僅以文件頭聲明的編碼去解釋你的代碼,加載到內存後,並不會主動幫你轉爲unicode,也就是說,你的文件編碼是utf-8,
加載到內存裏,你的變量字符串就也是utf-8, 這意味着什麼?意味着,你以utf-8編碼的文件,在windows是亂碼。
  其實亂是正常的,不亂纔不正常,因爲只有2種情況 ,你的windows上顯示纔不會亂。
Python2並不會自動的把文件編碼轉爲unicode存在內存裏。
1、字符串以GBK格式顯示
2、字符串是unicode編碼
所以我們只有手動轉,Python3 自動把文件編碼轉爲unicode必定是調用了什麼方法,這個方法就是,decode(解碼) 和encode(編碼)。
方法如下:
  UTF-8/GBK --> decode 解碼 --> Unicode

  Unicode --> encode 編碼 --> GBK / UTF-8 
  例如:

 

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# write by congcong

s = '匆匆'
print(s)
s1 = s.decode("utf-8") # utf-8 轉成 Unicode,decode(解碼)需要註明當前編碼格式
print(s1,type(s1))

s2 = s1.encode("gbk") # unicode 轉成 gbk,encode(編碼)需要註明生成的編碼格式
print(s2,type(s2))

s3 = s1.encode("utf-8") # unicode 轉成 utf-8,encode(編碼)註明生成的編碼格式
print(s3,type(s3))

 

  文件在 Python2 和 Python3 環境下運行結果的區別,如下所示:

 

#coding:utf-8
s = "你好,中國!" 
print(s)     # Python2輸出亂碼,Python3正常輸出
print(type(s))    # 均輸出 <type 'str'>

#解碼成unicode
s1 = s.decode("utf-8")
print(s1)    # Python2中輸出 “你好,中國!”,Python3顯示'str'對象沒有屬性'decode'
print(type(s1))   # Python2中輸出 <type 'unicode'>  Python3中輸出 <class 'str'> 

#編碼成gbk 或 utf-8
s2 = s1.encode('gbk')
print(s2)      # Python2中輸出 “你好,中國!”
print(type(s2))    # Python2中輸出 <type 'str'>
s3 = s1.encode('utf-8')
print(s3)      # Python2輸出亂碼,
print(type(s3))    # 輸出 <type 'str'>

複製代碼

編碼相互轉換的規則如下:

 

四、如何驗證編碼轉對了呢?
  1、查看數據類型,python 2 裏有專門的unicode 類型
  2、查看unicode編碼映射表
  unicode字符是有專門的unicode類型來判斷的,但是utf-8,gbk編碼的字符都是str,你如果分辨出來的當前的字符串數據是何種編碼的呢? 
  有人說可以通過字節長度判斷,因爲utf-8一箇中文佔3字節,gbk一個佔2字節。
  看輸出的字節個數,也能大體判斷是什麼類型。精確的驗證一個字符的編碼呢,就是拿這些16進制的數跟編碼表裏去匹配。

  關於 Unicode 與 GBK 等編碼對應關係(以中文“路”爲例):

    


完整的編碼對應表可到這個網站下載:unicode與gbk的映射表 http://www.unicode.org/charts/
  



五、Python  bytes類型

  把8個二進制一組稱爲一個byte,用16進制來表示。爲的就是讓人們看起來更可讀。我們稱之爲bytes類型,即字節類型。
  python2的字符串其實更應該稱爲字節串。 通過存儲方式就能看出來, 但python2裏還有一個類型是bytes呀,難道又叫bytes又叫字符串?
   嗯 ,是的,在python2裏,bytes == str , 其實就是一回事。
  除此之外呢, python2裏還有個單獨的類型是unicode , 把字符串解碼後,就會變成unicode。

複製代碼

>>> s
'\xe8\xb7\xaf\xe9\xa3\x9e' #utf-8
>>> s.decode('utf-8')
u'\u8def\u98de' #unicode 在unicode編碼表裏對應的位置
>>> print(s.decode('utf-8'))
路飛 #unicode 格式的字符 

複製代碼


 
Python2的默認編碼是ASCII碼,當後來大家對支持漢字、日文、法語等語言的呼聲越來越高時,Python於是準備引入unicode,但若直接把默認編碼改成unicode的話是不現實的, 因爲很多軟件就是基於之前的默認編碼ASCII開發的,編碼一換,那些軟件的編碼就都亂了。所以Python 2就直接搞了一個新的字符類型,就叫unicode類型,比如你想讓你的中文在全球所有電腦上正常顯示,在內存裏就得把字符串存成unicode類型。

複製代碼

>>> s = "路飛"
>>> s
'\xe8\xb7\xaf\xe9\xa3\x9e'
>>> s2 = s.decode("utf-8")
>>> s2
u'\u8def\u98de'
>>> type(s2)
<type 'unicode'>

複製代碼


 

 注意: 

  Python3 除了把字符串的編碼改成了unicode, 還把str 和bytes 做了明確區分, str 就是unicode格式的字符, bytes就是單純二進制啦。

  在py3裏看字符,必須得是unicode編碼,其它編碼一律按bytes格式展示。

  Python只要出現各種編碼問題,無非是哪裏的編碼設置出錯了

常見編碼錯誤的原因有以下這些: 

  • Python解釋器的默認編碼
  • Python源文件文件編碼
  • Terminal使用的編碼
  • 操作系統的語言設置
最後總結一下:
 python3   文件默認編碼是utf-8 , 字符串編碼是 unicode
       以utf-8 或者 gbk等編碼的代碼,加載到內存,會自動轉爲unicode正常顯示。

 python2   文件默認編碼是ascii , 字符串編碼也是 ascii , 如果文件頭聲明瞭是gbk,那字符串編碼就是gbk。
       以utf-8 或者 gbk等編碼的代碼,加載到內存,並不會轉爲unicode,編碼仍然是utf-8或者gbk等編碼。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章