請問哪位大牛能詳細而又通俗的解釋下,
Python2下unicode、utf-8、decode、encode之間的關係。
我感覺我在這方面的認識還不夠清晰,希望大牛們能幫幫忙,謝謝!!
回複內容:
py2的編碼其實是最最貼近實際的編碼形式了。反倒是py3,如果遇到個編碼標記錯誤之類的問題,直接讓你自殺……
先說編碼是什麼:我們知道電腦裡儲存任何資料都是儲存的二進位,但是一串文字若是當圖片那樣儲存太浪費空間不說,也會難以解析,所以ascii標準碼使用了7位二進位標記了128個字元和控制符號。當然7位不利於資料對齊,所以乾脆以8位儲存,最高位補個0就好,正好一個位元組,此即為基礎ascii編碼。
但是這128個字元裡,雖然包含了常見英文符號和必須的控制符號(比如換行、斷行符號、EOLN、EOF),卻對使用其它語言的使用者而言沒法用,畢竟各家字元不同哇……
首先是歐系拉丁語系指出,既然一個位元組一個字元,只用到7位,那麼還有128個編號可以用,於是規定了相應的拉丁語系主要符號,同樣單位元組表示,這樣就用到了多出來的一位,這套編碼稱之為latin-1
再往後,大多數其他拼音語言的國家表示,我們不用拉丁文符號,那麼把那128個額外字元改成別的符號,映射自己的文字就沒問題了。於是出現了多編碼頁,也就是最初的codepage。
但是中日韓為首的字形語言系的國家不行啊,你們丫的就幾十個符號,可中文之類光常用字就好幾千啊……於是針對中文出現了codepage936/gb2312,通過兩個位元組表示一個漢字,其中包含數千常用字,並且規定最高位為0的部分完全相容ascii,但是若最高位為1,則必須是兩個位元組連續出現,用以表示一個漢字——隨後還出現了GBK,規定的字元更多,相容gb2312,同樣是個雙位元組紀錄。
然而有兩件事情形成了阻礙:一是中文博大精深,漢字實在太多,算上生僻字,兩個位元組其實也不夠用;另一方面,在GB系編碼下,所有雙位元組字元都會被解釋成漢字,因此最多做到英漢混排,多語言沒戲,同時還會影響到諸如網路傳輸等等情境,因為同樣的雙位元組位元據,對應GBK中文與對應的日文韓文顯然不同,這就必須帶著編碼類別型跑,稍不注意就不知道是個啥語言的玩意。
於是出現了unicode,是ANSI標準下的多國語言文字編碼。unicode使用32位二進位表示每一個字元,且任意語言任意符號都有獨立編碼,這樣就可以做到使用一套編碼同時處理多種不同語言。
unicode是個編碼方式,只涉及編號,並不管傳輸和儲存。針對需求,unicode產生了若干傳輸用編碼,其中比較普及的有utf32,utf16和utf8。utf32是每字元32位固定編碼,完整映射unicode原編碼而不做改變(當然,規定了一下傳輸時的端序問題);utf16則是最少16位最多32位,屬於變長unicode傳輸方案,以實現對部分codepage的相容;而utf-8則是最小8位最大32位的編碼,變長,且英文部分完全相容ascii。由於省空間及ascii相容這兩點,使得改用utf8代價最小,才成為了主流。
python2裡,與編碼有關的有三個部分:
一是原始碼識別問題。原本python解譯器純粹把源碼使用ascii編碼進行解析產生文法樹。考慮到源碼裡可能存在其他語言的字串量,提供了setdefaultencode介面,但是非常容易引發各類問題。PEP263指出在檔案第一行或者第二行(僅限第一行為Unix指令碼標註的情況下)寫入特殊格式的注釋# coding:xxx可以指定解譯器解釋源碼時使用的字元編碼。
第二部分則是內建類型轉換:python2裡的str類,其實是個不儲存編碼資訊的類型。也就是說它把內容二進位按照逐個位元組來處理和比對、運算。str類型的「字串」如果拿來迭代一下,會直接拆成一個個位元組來處理。但是,一旦我們需要對非單位元組編碼的單個字進行處理的時候,python只提供了一個類型來解決問題,即unicode類(注意,實質上py裡這個類是utf8進行記憶體儲存的,並不是utf32/unicode原編碼),所以常常需要相互轉換,將用到encode/decode兩個方法。原則上是,decode方法是將一個str按照指定編碼解析後轉換為unicode,encode方法則是把一個unicode對象用指定編碼錶示並儲存到一個str對象裡。
第三點是輸入輸出。Python2的print的實質是將str裡的東西輸出到PIPE,如果你print的是一個unicode對象,它會自動根據LOCALE環境變數進行encode之後變成str再輸出。然而一般在Windows上都沒有設定locale環境變數,py2就按照預設的ascii編碼進行處理,於是對中文自然就編碼錯誤了。解決方案是手動encode成對應的輸出端可接受的編碼後輸出。win下一般都是gbk,linux下一般都是utf8
py3中的str則是unicode,bytes才類似於原str,預設代碼解析用utf8,預設輸出編碼也是utf8。
ASCII 、unicode 是字元集,utf-8是字元集的編碼方式。
utf-8 是 unicode 字元集一種編碼方式。
如果你不指定py檔案的編碼方式,程式預設按照 ASCII 字元集來解碼。所以需要聲明檔案編碼方式。
decode 和 encode
In [1]: a='你好'In [2]: aOut[2]: '\xe4\xbd\xa0\xe5\xa5\xbd'In [3]: b=a.decode('utf-8')In [4]: bOut[4]: u'\u4f60\u597d'In [5]: type(b)Out[5]: unicodeIn [6]: type(a)Out[6]: strIn [7]: c=b.encode('utf-8')In [8]: cOut[8]: '\xe4\xbd\xa0\xe5\xa5\xbd'In [9]: c==aOut[9]: True
善於搜尋,參考廖雪峰的部落格:字串和編碼
這個你搜網上很多啊。 unicode是codepoint 就是一個抽象的\uxxx 代表一個字元。 而utf-8是unicode的一種,用x個位元組表示一個抽象的codepoint \uxxx. 所以utf-8是實際的位元組串,而unicode是抽象. 你可以把抽象的unicode encode(編碼)成utf-8. 也可以把實際的utf-8 解碼回unicode. 說了這麼多,然並ruan...請搜尋“將python2中漢字會出現亂碼的事一次性說清楚”
看看!用 python3 吧,最近用 python, 已經被2.x 的編碼問題搞的要懷疑人生了,真是坑死人。。
http://nedbatchelder.com/text/unipain.html
這篇文章不錯~