前言:中文編碼問題一直是程序員頭疼的問題,而Python2中的字符編碼足矣令新手抓狂。本文將盡可能用通俗的語言帶你們完全的瞭解字符編碼以及Python2和3中的各類編碼問題。python
1、什麼是字符編碼。程序員
要完全解決字符編碼的問題就不能不去了解到底什麼是字符編碼。計算機從本質上來講只認識二進制中的0和1,能夠說任何數據在計算機中實際的物理表現形式也就是0和1,若是你將硬盤拆開,你是看不到所謂的數字0和1的,你能看到的只是一塊光滑閃亮的磁盤,若是你用足夠大的放大鏡你就能看到磁盤的表面有着無數的凹凸不平的元件,凹下去的表明0,突出的表明1,這就是計算機用來表現二進制的方式。windows
1.ASCII數組
如今咱們面臨了第一個問題:如何讓人類語言,好比英文被計算機理解?咱們以英文爲例,英文中有英文字母(大小寫)、標點符號、特殊符號。若是咱們將這些字母與符號給予固定的編號,而後將這些編號轉變爲二進制,那麼計算機明顯就可以正確讀取這些符號,同時經過這些編號,計算機也可以將二進制轉化爲編號對應的字符再顯示給人類去閱讀。由此產生了咱們最熟知的ASCII碼。ASCII 碼使用指定的7 位或8 位二進制數組合來表示128 或256 種可能的字符。這樣在大部分狀況下,英文與二進制的轉換就變得容易多了。性能
2.GB2312學習
然而,雖然計算機是美國人發明的,可是全世界的人都在使用計算機。如今出現了另外一個問題:如何讓中文被計算機理解?這下麻煩了,中文不像拉丁語系是由固定的字母排列組成的。ASCII 碼顯然沒辦法解決這個問題,爲了解決這個問題中國國家標準總局1980年發佈《信息交換用漢字編碼字符集》提出了GB2312編碼,用於解決漢字處理的問題。1995年又頒佈了《漢字編碼擴展規範》(GBK)。GBK與GB 2312—1980國家標準所對應的內碼標準兼容,同時在字彙一級支持ISO/IEC10646—1和GB 13000—1的所有中、日、韓(CJK)漢字,共計20902字。這樣咱們就解決了計算機處理漢字的問題了。測試
3.Unicode編碼
如今英文和中文問題被解決了,但新的問題又出現了。全球有那麼多的國家不只有英文、中文還有阿拉伯語、西班牙語、日語、韓語等等。難不成每種語言都作一種編碼?基於這種狀況一種新的編碼誕生了:Unicode。Unicode又被稱爲統一碼、萬國碼;它爲每種語言中的每一個字符設定了統一而且惟一的二進制編碼,以知足跨語言、跨平臺進行文本轉換、處理的要求。Unicode支持歐洲、非洲、中東、亞洲(包括統一標準的東亞象形漢字和韓國表音文字)。這樣無論你使用的是英文或者中文,日語或者韓語,在Unicode編碼中都有收錄,且對應惟一的二進制編碼。這樣你們都開心了,只要你們都用Unicode編碼,那就不存在這些轉碼的問題了,什麼樣的字符都可以解析了。操作系統
4.UTF-83d
可是,因爲Unicode收錄了更多的字符,可想而知它的解析效率相比ASCII碼和GB2312的速度要大大下降,並且因爲Unicode經過增長一個高字節對ISO Latin-1字符集進行擴展,當這些高字節位爲0時,低字節就是ISO Latin-1字符。對能夠用ASCII表示的字符使用Unicode並不高效,由於Unicode比ASCII佔用大一倍的空間,而對ASCII來講高字節的0對他毫無用處。爲了解決這個問題,就出現了一些中間格式的字符集,他們被稱爲通用轉換格式,即UTF(Unicode Transformation Format)。而咱們最經常使用的UTF-8就是這些轉換格式中的一種。在這裏咱們不去研究UTF-8究竟是如何提升效率的,你只須要知道他們之間的關係便可。
總結:
**1.爲了處理英文字符,產生了ASCII碼。
2.爲了處理中文字符,產生了GB2312。
3.爲了處理各國字符,產生了Unicode。
4.爲了提升Unicode存儲和傳輸性能,產生了UTF-8,它是Unicode的一種實現形式。**
2、Python2中的字符編碼
1.Python2中默認的字符編碼是ASCII碼,也就是說Python在處理數據時,只要數據沒有指定它的編碼類型,Python默認將其當作ASCII碼來進行處理。這個問題最直接的表如今當咱們編寫的python文件中包含有中文字符時,在運行時會提示出錯。如圖:
這個問題出現的緣由是:Python2會將整個python腳本中的內容當作ASCII碼去處理,當腳本中出現了中文字符,好比這裏的「小明」,咱們知道ASCII碼是不可以處理中文字符的,因此出現了這個錯誤。解決的辦法是:在文件頭部加入一行編碼聲明,如圖:
這樣,Python在處理這個腳本時,會用UTF-8的編碼去處理整個腳本,就可以正確的解析中文字符了。
2.Python2中字符串有str和unicode兩種類型。
上圖中展示出了Python2中字符串的兩種類型:
name變量被賦予了一個字符串「小明」;
unicode_name是name變量的unicode格式,這裏咱們使用了decode()方法,咱們會在後面的內容中詳細講解;
二者在終端中返回了不一樣的字節串,type返回了不一樣的數據類型,但print打印出了相同的輸出。
這裏咱們注意到一個「字節串」的名稱,字節串是指該字符串在python中的標準形式,也就是說不管一個字符串是什麼樣的編碼,在python中都會有一串字節串來進行表示。字節串是沒有編碼的,對應的最終交給計算機處理的數據形式。
3.Python2中能夠直接查看到unicode的字節串。
在上圖中,輸入unicode_name的返回值,是一個unicode字節串,咱們可以直接看到這個字節串。而在python3中,咱們將不能直接看到unicode字節串,它會被顯示爲中文的「小明」;由於python3默認使用unicode編碼,unicode字節串將被直接處理爲中文顯示出來。
總結:
**1.Python2中默認的字符編碼是ASCII碼。
2.Python2中字符串有str和unicode兩種類型。str有各類編碼的區別,unicode是沒有編碼的標準形式。
3.Python2中能夠直接查看到unicode的字節串。**
3、decode()與encode()方法
前面咱們說了這麼多都是爲了這一節作鋪墊,如今咱們開始來處理Python2中的字符編碼問題。咱們首先要學習Python爲咱們提供的兩個轉換編碼的方法decode()與encode()。
***decode()方法將其餘編碼字符轉化爲Unicode編碼字符。
encode()方法將Unicode編碼字符轉化爲其餘編碼字符。*
話很少說,直接上圖:
chardet模塊能夠檢測字符串編碼,沒有該模塊的能夠用pip install chardet安裝。
首先解釋一下爲何name=」小明」 這裏的小明是一個utf-8編碼的字符。由於我使用的是Ubuntu14.04操做系統,系統默認的字符編碼就是UTF-8,因此當我在終端將一箇中文輸入時,系統就會自動將這個中文字符以UTF-8的編碼傳遞給Python。因此若是你的系統是windows操做系統,而大多數狀況下windows的系統編碼默認是gb2312,那麼在windows下作上圖的測試「小明」這個字符就是gb2312編碼。
上圖中咱們將utf-8編碼的name經過decode()方法轉換爲unicode_name,而後經過encode()方法將unicode_name轉換爲gb2312_name。這時咱們再用print去輸出gb2312編碼的字符時缺產生了一個奇怪的輸出。這是由於個人操做系統使用的是UTF-8編碼,對於gb2312編碼的字符天然不可以正確解析,若是咱們將該gb2312的字節串放在windows下輸出就可以獲得咱們想要的中文,如圖:
所謂亂碼本質上是系統編碼與所提供字符的編碼不一致致使的,咱們舉一個例子:
小明的電腦中存了一個utf-8的字母A,存儲在計算機中是1100001;
小紅的電腦中也存了一個gb2312的字母A,存儲在計算機中是11000010;
當小明與小紅交換信息時,各自的計算機就不會把對方傳遞過來的A識別爲字母A,可能認爲這是字母B。
因此當咱們須要操做系統正確的輸出一個字符時,除了要知道該字符的字符編碼,也要知道本身系統所使用的字符編碼。若是系統使用的是UTF-8編碼,處理的倒是gb2312的字符就會出現所謂「亂碼」。
一個Tips:
decode()方法與在字符串前加u的方法實現的效果相同好比u’小明’
總結:
1.Python2的對於字符編碼的轉換要以unicode做爲「中間人」進行轉化。
2.知道本身系統的字符編碼(Linux默認utf-8,Windows默認GB2312),對症下藥。
4、一個字符編碼的例子
在Linux操做系統下使用python2下獲取網易首頁的title,並以正確的中文顯示出來。
163的首頁使用的字符編碼是gb2312,而咱們前面提到過Linux下的默認字符編碼爲UTF-8,咱們測試一下直接提取會不會出現亂碼問題。
咱們發現確實提取到的title並不能正確顯示,由於網頁中已經聲明瞭它是一個gb2312的字符編碼,而個人系統中默認的字符編碼爲UTF-8顯然,我必需要將title轉換爲UTF-8的字符。
其實因爲utf-8屬於unicode字符編碼,在Linux中咱們能夠直接打印出unicode編碼的字符。如:
如今咱們在Windows用Python2來作另外一個實驗,此次咱們換成百度首頁的title:
此次咱們發現網頁上的字符編碼爲utf-8,那麼我在Windows下會不會出現亂碼:
因此咱們再次強調:亂碼本質上是系統編碼與所提供字符的編碼不一致致使的
在Pyhon3中字符編碼有了很大改善最主要的有如下幾點:
1.Python 3的源碼.py文件 的默認編碼方式爲UTF-8,因此在Python3中你能夠不用在py腳本中寫coding聲明,而且系統傳遞給python的字符再也不受系統默認編碼的影響,統一爲unicode編碼。
2.將字符串和字節序列作了區別,字符串str是字符串標準形式與2.x中unicode相似,bytes相似2.x中的str有各類編碼區別。bytes經過解碼轉化成str,str經過編碼轉化成bytes。
PS:有一個小問題被許多新手所困擾,咱們來看一下圖片:
咱們看到當一箇中文字符出如今一個list(或tuple、dict)中時,它並不會被顯示爲一箇中文而是字節串。但當該字符串從list中提取出來再print時就可以正常顯示爲中文。字節串是全部字符在python中的「本質」形態,因此你能夠簡單的理解爲list中呈現出的字節串是給計算機看的。