1. 程式人生 > >python字串編碼

python字串編碼

python預設編碼

  python 2.x預設的字元編碼是ASCII,預設的檔案編碼也是ASCII

  python 3.x預設的字元編碼是unicode,預設的檔案編碼是utf-8

 

中文亂碼問題

  無論以什麼編碼在記憶體裡顯示字元,存到硬碟上都是進位制,所以編碼不對,程式就會出錯。

  常見編碼有ascii編碼(美國),GBK編碼(中國),shift_JIS編碼(日本),unicode(統一編碼)等。

  需要注意的是,存到硬碟上時是以何種編碼存的,再從硬碟上讀出來時,就必須以何種編碼讀,要不然就會出現亂碼問題

  常見的編碼錯誤的原因有如下,出現亂碼時,按照編碼之前的關係,挨個排錯就能解決問題。

     python直譯器的預設編碼

     Terminal使用的編碼

     python原始檔檔案編碼

    作業系統的語言設定

  Python支援中文的編碼:utf-8gbkgb2312uft-8國際通用常用有資料庫、編寫程式碼gbkwindowscmd使用

 

編碼轉換

  如果想要中國的軟體可以正常的在美國人的電腦上實現,有下面兩種方法:

      讓美國人的電腦都裝上gbk編碼

      讓你的軟體編碼以utf-8編碼

   第一種方法不可現實,第二種方法比較簡單,但是也只能針對新開發的軟體,如果之前開發的軟體就是以

gbk的編碼寫的,上百萬行程式碼已經寫出去了,重新編碼成utf-8格式也會費很大力氣。

所以,針對已經用gbk開發的軟體專案如何進行編碼轉換,利用unicode的一個包含了跟全球所有國家編碼對映關係功能,就可以實現編碼轉換。無論什麼編碼儲存的資料,只要我們的軟體把資料從硬碟上讀到記憶體,轉成unicode來顯示即可,由於所有的系統程式語言都預設支援unicode,所有我們的gbk編碼軟體放在美國電腦上,載入到記憶體裡面,變成了unicode,中文就可正常展示

  類似用如下的轉碼的過程:

    源有編碼 -> unicode編碼 -> 目的編碼

    decode("UTF-8") 解碼

--> unicode  --> encode("gbk") 編碼

 

#_*_coding:utf-8_*_  的作用

  python2檔案中,經常在檔案開頭看到#_*_coding:utf-8 _*_ ”語句,它的作用是告訴python直譯器此.py檔案是utf-8編碼,需要用utf-8編碼去讀取這個.py檔案

 

python2.xbytes與python3.xbytes的區別

  Python2string處理為原生的bytes型別,而不是 unicode。而Python3所有的 string均是unicode型別。

  在python2.x中,寫字串,比如

 

>>>s = ”學習“
>>>print s
學習
>>>s    # 位元組型別
'\xd1\xa7\xcf\xb0'

 

  雖然說列印的是中文學習,但是直接呼叫變數s顯示的卻是一個個16進製表示的二進位制位元組,我們稱這個為byte型別,即位元組型別,它把8個二進位制組成一個byte,用16進製表示

  所以說python2.x的字串其實更應該稱為字串,通過儲存的方式就能看出來,但是在python2.x中還有一個bytes型別,兩個是否相同呢,回答是肯定的,在python2.x中,bytes==str

  python3.x把字串變成了unicode,檔案預設編碼utf-8這意味著,只要用python3.x,無論我們的程式以那種語言開發,都可以在全球各國電腦上正常顯示。

  python3.x除了把字串的編碼改成了unicode,還把strbytes做了明確區分,str就是unicode格式的字串,而bytes就是單純的二進位制(補充一個問題,在python3.x中,只要unicode編碼,字串就變成了bytes格式,不直接列印成gbk的字元,我覺得就是想通過這樣的方式明確的告訴你,想在python3.x中看字元,必須是unicode,其他編碼一律是bytes格式)

 

深入中文編碼問題

  python3內部使用的是unicode編碼,而外部卻要面對千奇百怪的各種編碼,比如作為中國程式經常要面對的gbkgb2312utf8等,那這些編碼是怎麼轉換成內部的unicode呢?

  首先看一下原始碼檔案中使用字串的情況。原始碼檔案作為文字檔案就必然是以某種編碼形式儲存程式碼的,python2預設原始碼檔案是asci編碼,python3預設原始碼檔案是utf-8編碼。比如python2程式碼檔案一個變數賦值:

    s1 = 'a'

    print s1

  python2認為這個字元'a'就是一個asci編碼的字元,這個檔案可以正常執行,並打印出'a'字元。在僅僅使用英文字元的情況下一切正常,但是如果用了中文,比如:

    s1 = '哈哈'

    print s1

  這個程式碼檔案被執行時就會出錯,就是編碼出了問題。python2預設將程式碼檔案內容當作asci編碼處理,但asci編碼中不存在中文,因此丟擲異常。

  解決問題之道就是要讓python2直譯器知道檔案中使用的是什麼編碼形式,對於中文,可以用的常見編碼有utf-8gbkgb2312等。只需在程式碼檔案的最前端新增如下:

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

  這就是告知python2直譯器,這個檔案裡的文字是用utf-8編碼的這樣,python就會依照utf-8的編碼形式解讀其中的字元,然後轉換成unicode編碼內部處理使用。

  不過,如果你在Windows控制檯下執行此程式碼的話,雖然程式是執行了,但螢幕上打印出的卻不是哈哈字。這是由於python2編碼與控制檯編碼的不一致造成的。Windows下控制檯中的編碼使用的是gbk,而在程式碼中使用的utf-8python2按照utf-8編碼列印到gbk編碼的控制檯下自然就會不一致而不能打印出正確的漢字。

  解決辦法一個是將原始碼的編碼也改成gbk,也就是程式碼第一行改成:

    # -*- coding: gbk -*-

  另一種方法是保持原始碼檔案的utf-8不變,而是在’哈哈’前面加個u字,也就是:

    s1=u’哈哈’

    print s1

  這樣就可以正確打印出’哈哈’字了。這裡的這個u表示將後面跟的字串以unicode格式儲存。python2會根據程式碼第一行標稱的utf-8編碼識別程式碼中的漢字’哈哈’,然後轉換成unicode物件。如果我們用type檢視一下’哈哈’的資料型別type(‘哈哈’),會得到<type str>,而type(u’哈哈’),則會得到<type unicode>

 

>>> type('哈哈')
<type 'str'>
>>> type(u'哈哈')
<type 'unicode'>

 

  也就是在字元前面加u就表明這是一個unicode物件,這個字會以unicode格式存在於記憶體中,而如果不加u,表明這僅僅是一個使用某種編碼的字串,編碼格式取決於python2對原始碼檔案編碼的識別,這裡就是utf-8

  Python2在向控制檯輸出unicode物件的時候會自動根據輸出環境的編碼進行轉換,但如果輸出的不是unicode物件而是普通字串,則會直接按照字串的編碼輸出字串,從而出現上面的現象。

  使用unicode物件的話,除了這樣使用u標記,還可以使用unicode類以及字串的encodedecode方法。

  unicode類的建構函式接受一個字串引數和一個編碼引數,將字串封裝為一個unicode,比如在這裡,由於我們用的是utf-8編碼,所以unicode中的編碼引數使用'utf-8'將字元封裝為unicode物件,然後正確輸出到控制檯:

    s1=unicode(‘’, ‘utf-8′)

    print s1

  另外,用decode函式也可以將一個普通字串轉換為unicode物件。很多人都搞不明白python2字串的decodeencode函式都是什麼意思。這裡簡要說明一下。

  decode函式是將普通字串按照引數中的編碼格式進行解析,然後生成對應的unicode物件,比如在這裡我們程式碼用的是utf-8,那麼把一個字串轉換為unicode物件就是如下形式:

>>> s2 = '哈哈'.decode('utf-8')
>>> type(s2)
<type 'unicode'>

  這時,s2就是一個儲存了’哈哈’字串unicode物件,其實就和unicode(‘哈哈’, utf-8)以及u’哈哈’是相同的。

  encode函式正好就是相反的功能,是將一個unicode物件轉換為引數中編碼格式的普通字元,比如下面程式碼:

>>> s3 = unicode('哈哈', 'utf-8').encode('utf-8')
>>> type(s3)
<type 'str'>
或者:
>>> s3 = '哈哈'.decode('utf-8').encode('utf-8')
>>> type(s3)
<type 'str'>

  s3現在又變回了utf-8的’哈’。同樣的,也可指定其它編碼格式,但要注意的是,用什麼格式編碼,就用什麼格式解碼,否則會出現中文亂碼問題。

  

字元編碼

  目前使用的編碼方式有:ASCII碼(一個位元組)、Unicode碼(兩個位元組)、UTF-8碼(可變長的編碼)我們已經知道了,字串也是一種資料型別,但是,字串比較特殊的是還有一個編碼問題。

  因為計算機只能處理數字,如果要處理文字,就必須先把文字轉換為數字才能處理。最早的計算機在設計時採用8個位元(bit)作為一個位元組(byte),所以,一個位元組能表示的最大的整數就是255(二進位制11111111=十進位制255),如果要表示更大的整數,就必須用更多的位元組。比如兩個位元組可以表示的最大整數是655354個位元組可以表示的最大整數是4294967295

  由於計算機是美國人發明的,因此,最早只有127個字元被編碼到計算機裡,也就是大小寫英文字母、數字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母A的編碼是65,小寫字母z的編碼是122但是要處理中文顯然一個位元組是不夠的,至少需要兩個位元組,而且還不能和ASCII編碼衝突,所以,中國製定了GB2312編碼,用來把中文編進去。可以想得到的是,全世界有上百種語言,日本把日文編到Shift_JIS裡,韓國把韓文編到Euc-kr裡,各國有各國的標準,就會不可避免地出現衝突,結果就是,在多語言混合的文字中,顯示出來會有亂碼。因此,Unicode應運而生。Unicode把所有語言都統一到一套編碼裡,這樣就不會再有亂碼問題了。Unicode標準也在不斷髮展,但最常用的是用兩個位元組表示一個字元(如果要用到非常偏僻的字元,就需要4個位元組)。現代作業系統和大多數程式語言都直接支援Unicode

  現在,捋一捋ASCII編碼和Unicode編碼的區別:ASCII編碼是1個位元組,而Unicode編碼通常是2個位元組。

  字母AASCII編碼是十進位制的65,二進位制的01000001

  字元'0'ASCII編碼是十進位制的48,二進位制的00110000,注意字元'0'和整數0是不同的;

  漢字中已經超出了ASCII編碼的範圍,用Unicode編碼是十進位制的20013,二進位制的01001110 00101101

  可以猜測,如果把ASCII編碼的AUnicode編碼,只需要在前面補0就可以,因此,AUnicode編碼是00000000 01000001

  新的問題又出現了:如果統一成Unicode編碼,亂碼問題從此消失了。但是,如果你寫的文字基本上全部是英文的話,用Unicode編碼比ASCII編碼需要多一倍的儲存空間,在儲存和傳輸上就十分不划算。

所以,本著節約的精神,又出現了把Unicode編碼轉化為“可變長編碼”的UTF-8編碼。UTF-8編碼把一個Unicode字元根據不同的數字大小編碼成1-6個位元組,常用的英文字母被編碼成1個位元組,漢字通常是3個位元組,只有很生僻的字元才會被編碼成4-6個位元組。如果你要傳輸的文字包含大量英文字元,用UTF-8編碼就能節省空間:

字元         ASCII                  Unicode                           UTF-8

 A         01000001        00000000 01000001               01000001

 中              x               01001110 00101101      11100100 10111000 10101101

UTF-8編碼有一個額外的好處,就是ASCII編碼實際上可以被看成是UTF-8編碼的一部分,所以,大量只支援ASCII編碼的歷史遺留軟體可以在UTF-8編碼下繼續工作。

 

編碼方式

1.ASCII

  現在我們面臨了第一個問題:如何讓人類語言,比如英文被計算機理解?我們以英文為例,英文中有英文字母(大小寫)、標點符號、特殊符號。如果我們將這些字母與符號給予固定的編號,然後將這些編號轉變為二進位制,那麼計算機明顯就能夠正確讀取這些符號,同時通過這些編號,計算機也能夠將二進位制轉化為編號對應的字元再顯示給人類去閱讀。由此產生了我們最熟知的ASCII碼。ASCII 碼使用指定的7 位或8 位二進位制數組合來表示128 256 種可能的字元。這樣在大部分情況下,英文與二進位制的轉換就變得容易多了。

 

2.GB2312

  雖然計算機是美國人發明的,但是全世界的人都在使用計算機。現在出現了另一個問題:如何讓中文被計算機理解?這下麻煩了,中文不像拉丁語系是由固定的字母排列組成的。ASCII 碼顯然沒辦法解決這個問題,為了解決這個問題中國國家標準總局1980年釋出《資訊交換用漢字編碼字符集》提出了GB2312編碼,用於解決漢字處理的問題。1995年又頒佈了《漢字編碼擴充套件規範》(GBK)。GBKGB 23121980國家標準所對應的內碼標準相容,同時在字彙一級支援ISO/IEC106461GB 130001的全部中、日、韓(CJK)漢字,共計20902字。這樣我們就解決了計算機處理漢字的問題了。

 

3.Unicode

  現在英文和中文問題被解決了,但新的問題又出現了。全球有那麼多的國家不僅有英文、中文還有阿拉伯語、西班牙語、日語、韓語等等。難不成每種語言都做一種編碼?基於這種情況一種新的編碼誕生了:UnicodeUnicode又被稱為統一碼、萬國碼;它為每種語言中的每個字元設定了統一併且唯一的二進位制編碼,以滿足跨語言、跨平臺進行文字轉換、處理的要求。Unicode支援歐洲、非洲、中東、亞洲(包括統一標準的東亞象形漢字和韓國表音文字)。這樣不管你使用的是英文或者中文,日語或者韓語,在Unicode編碼中都有收錄,且對應唯一的二進位制編碼。這樣大家都開心了,只要大家都用Unicode編碼,那就不存在這些轉碼的問題了,什麼樣的字元都能夠解析了。

 

4.UTF-8

  但是,由於Unicode收錄了更多的字元,可想而知它的解析效率相比ASCII碼和GB2312的速度要大大降低,而且由於Unicode通過增加一個高位元組對ISO Latin-1字符集進行擴充套件,當這些高位元組位為0時,低位元組就是ISO Latin-1字元。對可以用ASCII表示的字元使用Unicode並不高效,因為UnicodeASCII佔用大一倍的空間,而對ASCII來說高位元組的0毫無用處。為了解決這個問題,就出現了一些中間格式的字符集,他們被稱為通用轉換格式,即UTFUnicode Transformation Format)。而我們最常用的UTF-8就是這些轉換格式中的一種。在這裡我們不去研究UTF-8到底是如何提高效率的,你只需要知道他們之間的關係即可。