不想再被鄙視?那就看進來! 一文搞懂Python2字元編碼
程式設計師都自視清高,覺得自己是創造者,經常鄙視不太懂技術的產品或者QA。可悲的是,程式設計師之間也相互鄙視,程式設計師的鄙視鏈流傳甚廣,作為一個Python程式設計師,自然最關心的是下面這幅圖啦
我們專案組一值使用Python2.7,雖然我們也知道Python3的諸多好處,也曾經蠢蠢欲動過,但由於各種歷史原因,以及業務的壓力,我們只可能繼續使用Python2.7。更悲哀的是,我們組不是那麼international,所以程式碼中還是涉及到大量的中文,因此偶爾也會遇到亂碼以及UnicodeError,於是生活在了鄙視鏈的末端。
因此,本文的目標是解釋清楚python2.7中unicode、str的編解碼關係,力求在鄙視鏈中前進一步。
注意:本文實驗主要基於win7,Python2.7;以及Linux ,Python2.7。除非特殊說明,所有的命令都是在終端中互動式輸入;如果沒有強調平臺,那麼就是window上的結果。下面是一些預設的環境資訊(其重要性後文會介紹)
windows
Python1234567891011 | >>>importsys,locale>>>sys.getdefaultencoding()'ascii'>>>locale.getdefaultlocale()('zh_CN','cp936')>>>sys.stdin.encoding'cp936'>>>sys.stdout.encoding'cp936'>>>sys.getfilesystemencoding()'mbcs' |
Linux
Python1234567891011 | >>>importsys,locale>>>sys.getdefaultencoding()'ascii'>>>locale.getdefaultlocale()('zh_CN','UTF-8')>>>sys.stdin.encoding'UTF-8'>>>sys.stdout.encoding'UTF-8'>>>sys.getfilesystemencoding()'UTF-8' |
從字元編碼說起
首先來說一說gbk gb2312 unicode utf-8這些術語,這些術語與語言無關。
計算機的世界只有0和1,因此任何字元(也就是實際的文字元號)也是由01串組成。計算機為了運算方便,都是8個bit組成一個位元組(Byte),字元表達的最小單位就是位元組,即一個字元佔用一個或者多個位元組。字元編碼(character encoding)就是字集碼,編碼就是將字符集中的字元對映為一個唯一二進位制的過程。
計算機發源於美國,使用的是英文字母(字元),所有26個字母的大小寫加上數字0到10,加上符號和控制字元,總數也不多,用一個位元組(8個bit)就能表示所有的字元,這就是ANSI的“Ascii”編碼(American Standard Code for Information Interchange,美國資訊互換標準程式碼)。比如,小寫字母‘a’的ascii 碼是01100001,換算成十進位制就是97,十六進位制就是0x61。計算機中,一般都是用十六進位制來描述字元編碼。
但是當計算機傳到中國的時候,ASCII編碼就行不通了,漢字這麼多,一個位元組肯定表示不下啊,於是有了GB 2312(中國國家標準簡體中文字符集)。GB2312使用兩個位元組來對一個字元進行編碼,其中前面的一個位元組(稱之為高位元組)從0xA1用到 0xF7,後面一個位元組(低位元組)從0xA1到0xFE,GB2312能表示幾千個漢字,而且與asill嗎也是相容的。
但後來發現,GB2312還是不夠用,於是進行擴充套件,產生了GBK(即漢字內碼擴充套件規範), GBK同Gb2312一樣,兩個位元組表示一個字元,但區別在於,放寬了對低位元組的要求,因此能表示的範圍擴大到了20000多。後來,為了容納少數名族,以及其他漢字國家的文字,出現了GB13080。GB13080是相容GBK與GB2312的,能容納更多的字元,與GBK與GB2312不同的是,GB18030採用單位元組、雙位元組和四位元組三種方式對字元編碼
因此,就我們關心的漢字而言,三種編碼方式的表示範圍是:
GB18030 》 GBK 》 GB2312
即GBK是GB2312的超集,GB1803又是GBK的超集。後面也會看到,一個漢字可以用GBK表示,但不一定能被GB2312所表示
當然,世界上還有更多的語言與文字,每種文字都有自己的一套編碼規則,這樣一旦跨國就會出現亂碼,亟待一個全球統一的解決辦法。這個時候ISO(國際標準化組織)出馬了,發明了”Universal Multiple-Octet Coded Character Set”,簡稱 UCS, 俗稱 “unicode”。目標很簡單:廢了所有的地區性編碼方案,重新搞一個包括了地球上所有文化、所有字母和符號 的編碼!
unicode每種語言中的每個字元設定了統一併且唯一的二進位制編碼,以滿足跨語言、跨平臺進行文字轉換、處理的要求。unicode編碼一定以u開頭。
但是,unicode只是一個編碼規範,是所有字元對應二進位制的集合,而不是具體的編碼規則。或者說,unicode是表現形式,而不是儲存形式,就是說沒用定義每個字元是如何以二進位制的形式儲存的。這個就跟GBK這些不一樣,GBK是表裡如下,表現形式即儲存形式。
比如漢字“嚴”的unicode編碼是u4e25,對應的二進位制是1001110 00100101,但是當其經過網路傳輸或者檔案儲存時,是沒法知道怎麼解析這些二進位制的,容易和其他位元組混在一起。那麼怎麼儲存unicode呢,於是出現了UTF(UCS Transfer Format),這個是具體的編碼規則,即UTF的表現形式與儲存格式是一樣的。
因此,可以說,GBK和UTF-8是同一個層面的東西,跟unicode是另一個層面的東西,unicode飄在空中,如果要落地,需要轉換成utf-8或者GBK。只不過,轉換成Utf-8,大家都能懂,更懂用,而轉換成GBK,只有中國人才看得懂
UTF也有不同的實現,如UTF-8, UTF-16, 這裡以UTF-8為例進行講解(下面一小節引用了阮一峰的文章)。
unicode與utf-8
UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個位元組表示一個符號,根據不同的符號而變化位元組長度。UTF-8的編碼規則很簡單,只有二條:
1)對於單位元組的符號,位元組的第一位設為0,後面7位為這個符號的unicode碼。因此對於英語字母,UTF-8編碼和ASCII碼是相同的。
2)對於n位元組的符號(n>1),第一個位元組的前n位都設為1,第n+1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位制位,全部為這個符號的unicode碼。
下表總結了編碼規則,字母x表示可用編碼的位。
1234567 | Unicode符號範圍|UTF-8編碼方式(十六進位制)|(二進位制)----------------------+---------------------------------------------00000000-0000007F|0xxxxxxx00000080-000007FF|110xxxxx10xxxxxx00000800-0000FFFF|1110xxxx10xxxxxx10xxxxxx00010000-0010FFFF|11110xxx10xxxxxx10xxxxxx10xxxxxx |
以漢字“嚴”為例,演示如何實現UTF-8編碼。
已知“嚴”的unicode是4E25(100111000100101),根據上表,可以發現4E25處在第三行的範圍內(0000 0800-0000 FFFF),因此“嚴”的UTF-8編碼需要三個位元組,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然後,從“嚴”的最後一個二進位制位開始,依次從後向前填入格式中的x,多出的位補0。這樣就得到了,“嚴”的UTF-8編碼是“11100100 10111000 10100101”,轉換成十六進位制就是E4B8A5。
當編解碼遇上Python2.x
下面使用Python語言來驗證上面的理論。在這一章節中,當提到unicode,一般是指unicode type,即Python中的型別;也會提到unicode編碼、unicode函式,請大家注意區別。
另外,對於編碼,也有兩種意思。第一個是名字,指的是字元的二進位制表示,如unicode編碼、gbk編碼。第二個是動詞,指的是從字元到二進位制的對映過程。不過後文中,編碼作為動詞,狹義理解為從unicode型別轉換成str型別的過程,解碼則是相反的過程。另外強調的是,unicode型別一定是unicode編碼,而str型別可能是gbk、ascii或者utf-8編碼。
unicode 與 str 區別
在python2.7中,有兩種“字串”型別,分別是str 與 unicode,他們有同一個基類basestring。str是plain string,其實應該稱之為位元組串,因為是每一個位元組換一個單位長度。而unicode就是unicode string,這才是真正的字串,一個字元(可能多個位元組)算一個單位長度。
python2.7中,unicode型別需要在文字之間加u表示。
Python1234567 | >>>us=u'嚴'>>>printtype(us),len(us)<type'unicode'>1>>>s='嚴'>>>printtype(s),len(s)<type'str'>2>>> |
從上可以看到,第一,us、s的型別是不一樣的;其二,同一個漢字,不同的型別其長度也是不一樣的,對於unicode型別的例項,其長度一定是字元的個數,而對於str型別的例項,其長度是字元對應的位元組數目。這裡強調一下,s(s = ‘嚴’)的長度在不同的環境下是不一樣的!後文會解釋
__str__ __repr__的區別
這是python中兩個magic method,很容易讓新手迷糊,因為很多時候,二者的實現是一樣的,但是這兩個函式是用在不同的地方
_str__, 主要是用於展示,str(obj)或者print obj的時候呼叫,返回值一定是一個str 物件
__repr__, 是被repr(obj), 或者在終端直接打obj的時候呼叫
Python12345 | >>>us=u'嚴'>>>usu'\u4e25'>>>printus嚴 |
可以看到,不使用print返回的是一個更能反映物件本質的結果,即us是一個unicode物件(最前面的u表示,以及unicode編碼是用的u),且“嚴”的unicode編碼確實是4E25。而print呼叫可us.__str__,等價於print str(us),使得結果對使用者更友好。那麼unicode.__str__是怎麼轉換成str的呢,答案會在後面揭曉
unicode str utf-8關係
前面已經提到,unicode只是編碼規範(只是字元與二進位制的對映集合),而utf-8是具體的編碼規則(不僅包含字元與二進位制的對映集合,而且對映後的二進位制是可以用於儲存和傳輸的),即utf-8負責把unicode轉換成可儲存和傳輸的二進位制字串即str型別,我們稱這個轉換過程為編碼。而從str型別到unicode型別的過程,我們稱之為解碼。
Python中使用decode()和encode()來進行解碼和編碼,以unicode型別作為中間型別。如下圖所示
Python12 | decode encodestr--------->unicode--------->str |
即str型別呼叫decode方法轉換成unicode型別,unicode型別呼叫encode方法轉換成str型別。for example
Python12345678 | >>>us=u'嚴'>>>ss=us.encode('utf-8')>>>ss'\xe4\xb8\xa5'>>>type(ss)<type'str'>>>>ss.decode('utf-8')==usTrue |
從上可以看出encode與decode兩個函式的作用,也可以看出’嚴’的utf8編碼是E4B8A5。
就是說我們使用unicode.encode將unicode型別轉換成了str型別,在上面也提到unicode.__str__也是將unicode型別轉換成str型別。二者有什麼卻比呢
unicode.encode 與 unicode.__str__的區別
首先看看文件
Python123456 | str.encode([encoding[,errors]]) Returnan encoded version of the string.Default encoding isthe current default stringencoding.object.__str__(self) Called by the str()built-infunction andby the printstatement to compute the“informal”stringrepresentation of an object. |
注意:str.encode 這裡的str是basestring,是str型別與unicode型別的基類
可以看到encode方法是有可選的引數:encoding 和 errors,在上面的例子中encoding即為utf-8;而__str__是沒有引數的,我們可以猜想,對於unicode型別,__str__函式一定也是使用了某種encoding來對unicode進行編碼。
首先不禁要問,如果encode方法沒有帶入引數,是什麼樣子的:
Python1234 | >>>us.encode()Traceback(most recent call last):File"<stdin>",line1,in<module>UnicodeEncodeError:'ascii'codec can't encode character u'\u4e25'inposition0:ordinal notinrange(128) |
不難看出,預設使用的就是ascii碼來對unicode就行編碼,為什麼是ascii碼,其實就是系統預設編碼(sys.getdefaultencoding的返回值)。ascii碼顯然無法表示漢字,於是丟擲了異常。而使用utf-8編碼的時候,由於utf能夠表示這個漢字,所以沒報錯。
如果直接列印ss(us.encode(‘utf-8’)的返回值)會怎麼樣
Python12 | >>>printss涓 |
結果略有些奇怪,us.__str__(即直接列印us)的結果不一樣,那麼試試encoding = gbk呢?
Python12 | >>>printus.encode('gbk')嚴 |
U got it! 事實上也是如此,python會採用終端預設的編碼(用locale.getdefaultlocale()檢視,windows是為gbk)將unicode編碼成str型別。
在Linux(終端編碼為utf-8),結果如下:
Python12345678 | >>>us=u'嚴'>>>printus.encode('utf-8')嚴>>>printus.encode('gbk')▒▒>>>printus嚴>>> |
注意上面的亂碼!
unicode gbk之間的轉換
在上上小節,介紹了unicode可以通過utf-8編碼(encoding = utf-8),轉換成utf-8表示的str,在上一節也可以看出unicode也可以通過gbk編碼(encoding=gbk),轉換成gbk表示的str。這裡有點暈,留作第一個問題,後面解釋
unicode與utf8之間的相互轉換可以計算得知,但unicode與gbk之間的相互轉換沒有計算公式,就只能靠查表了,就是說有一張對映表,有某一個漢字對應的unicode表示與gbk表示的對映關係
Python12345678910111213 | >>>us=u'嚴'>>>usu'\u4e25'>>>us.encode('gbk')'\xd1\xcf'>>>us.encode('gb2312')'\xd1\xcf'>>>us.encode('gb18030')'\xd1\xcf'>>>s='嚴'>>>s'\xd1\xcf'>>> |
從上不難看出,嚴的unicdoe編碼是4e25,GBK編碼是d1cf,因此us通過gbk編碼就是d1cf。同樣也能看到,GB18030,GBK,GB2312是相容的
為什麼print us.encode(‘utf-8’)打印出“涓”
ss = us.encode(‘utf-8’), ss是一個str型別,直接列印結果有點奇怪,一個“涓”字,那一個str型別的“涓”是哪些二進位制組成的呢
Python123 | >>>s='涓'>>>s'\xe4\xb8' |
可以看到,str型別的“涓”,其二進位制是E4B8,跟’嚴’的utf8編碼(E4B8A5)相差了一個A5,那麼就是因為A5顯示不出來,驗證如下:
相關推薦
不想再被鄙視?那就看進來! 一文搞懂Python2字元編碼
程式設計師都自視清高,覺得自己是創造者,經常鄙視不太懂技術的產品或者QA。可悲的是,程式設計師之間也相互鄙視,程式設計師的鄙視鏈流傳甚廣,作為一個Python程式設計師,自然最關心的是下面這幅圖啦 我們專案組一值使用Python2.7,雖然我們也知道Python3的諸多好處,
w3-周六__今晚就好好解決掉用指針、用引用的困惑。以後不想再被這個問題煩到
strong pil AR 模糊 ron 問題 解決 clas enc ?: 1. 引用reference的本質: 常指針 ——> 什麽時候用指針?= 就按Java中的引用變量那樣用? ——> 什麽時候用引用? ①函數的入參/返回值時 ②T& p
【機器學習】機器學習Top10演算法,教你選擇最合適的那一個!一文讀懂ML中的解析解與數值解...
在機器學習領域裡,不存在一種萬能的演算法可以完美解決所有問題,尤其是像預測建模的監督學習裡。比方
一文搞懂高頻面試題之限流演算法,從演算法原理到實現,再到對比分析
限流是指在系統面臨高併發、大流量請求的情況下,限制新的流量對系統的訪問,從而保證系統服務的安全性。常用的限流演算法有計數器固定視窗演算法、滑動視窗演算法、漏斗演算法和令牌桶演算法,下面將對這幾種演算法進行分別介紹,並給出具體的實現。本文目錄如下,略長,讀者可以全文閱讀,同樣也可以只看感興趣的部分。 [TOC
jquery中checkbox選中取消後,不能再被jquery選中的問題!
選中 ttr 希望 後來 多看 我們 https show jquery 糾結了一上午,簡單的問題使用了幾種方法處理,最後還是失敗,後來查閱了羋老頭的資料才看到這麽一篇文章,這裏值得推薦給新手看一下。 https://www.cnblogs.com/Showshare/p/
windows 10 真的不想再用了
如題 先說最噁心的 作為一個IT黨,天天工作就是面對電腦,每天敲鍵盤寫程式碼,跑程式,但是經常一個程式跑一夜,第二天回來一看,半夜電腦重啟了,早上啥都沒了.大清早一臉懵逼. 再說預設瀏覽器 作為一個web的開發人員,ie的除錯工具真心不好用,每次將預設瀏覽器換成搜狗,或者
對於大於 11 的數,如果除了 11 和它本身,它不能再被其它正整數整除,那麼我們說它是一個質數。
對於大於 1 的數,如果除了 1 和它本身,它不能再被其它正整數整除,那麼我們說它是一個質數。曉萌想判斷一個數是不是質數,希望找你寫個程式,幫助她進行判斷。 輸入格式 輸入包括一行,為一個整數 N(1<N≤1000),正是曉萌給出你讓你判斷的數字。 輸出格式 輸出包
比爾·蓋茨:如果你想了解矽谷,就看《矽谷》吧
簡評:本文系譯文,這是 Bill Gates 為美劇《矽谷》撰寫的一篇文章,沒想到 Bill Gates 也在追這部超魔幻現實主義科技喜劇。劇中對科技行業的描繪恰如其分,劇組曾拜訪過 Bill Gates,並且還向 Gates 本人諮詢過本行業的歷史,但是沒有給他劇透哈哈 :) 考慮到矽谷對我們生
不想再WA了
https://ac.nowcoder.com/acm/contest/322/G 題解:預處理,給我搜,暴力就完事了 /* *@Author: STZG *@Language: C++ */ #include <bits/stdc++.h> #include<io
西南民族大學第十屆校賽(同步賽)(G題——不想再WA了)
題目描述 歡迎參加西南民族大學 2018 年校賽。 對於你來說,做題 WA 了 是一件很痛苦的事,所以你從現在開始不想再看到有題 WA 了。 那麼現在給你 A,C,W 三種字元,問組成一個長度為 n(不含 WA,即 W 後一個字元不能為 A ) 的字串,總共有多少種方案?( T
牛客網 - 不想再WA了(遞推)
題目連結:https://ac.nowcoder.com/acm/contest/322/G 時間限制:C/C++ 1秒,其他語言2秒 空間限制:C/C++ 32768K,其他語言65536K 64bit IO Format: %lld 題目描述 歡迎參加西南民族大學 2018 年校賽。
馬上大四了,不想再糊塗的過日子了。決定找實訓機構好好補補了
不知不覺大學生活就要過完了,突然感覺自己好迷茫。好像大學四年都是混過來的,自己什麼也沒學到。回想當初進大學時的激情、夢想。通通都在不知不覺中失落了。好想從頭來過,可惜一切都不在了! 大四,不想再在迷茫中度過,考研?感覺讀了10多年書,已經厭倦了,我還是想學東
我真的不想再用 JPA 了
在開發者的圈子裡,沒當說到一種技術好或者不好,都會引發激烈或者不激烈的爭論,直到一個開發者出來說 PHP 是世界上最好的語言,大家夥兒才會紛紛退去繼續寫程式碼。 今天說 JPA 的問題不是想引發什麼討論或者罵戰,單純的就是我不喜歡 JPA 。沒錯,就是這麼 Real。 說到 Java 開發,涉及到資料庫訪問的
【Spring】只想用一篇文章記錄@Value的使用,不想再找其它了(附思維導圖)
1 簡介 不得不說,Spring為大家提供許多開箱即用的功能,@Value就是一個極其常用的功能,它能將配置資訊注入到bean中去。即使是一個簡單的功能,Spring也提供了豐富的注入型別和形式。我經常會忘記一些特別型別注入的寫法,比如說陣列,現在整理一下,希望以後不用再找了。 2 三種形式 使用@Val
如何制定切實可行的計劃並好好執行——2020年,我不想再碌碌無為
1 簡介 轉眼2019年即將結束,又到了做年度總結和下一年計劃的時候了。經過多年的失敗經歷,是時候要總結出一些經驗教訓了。 本文整理了在計劃與執行方面的個人學習心得體會,與大家分享,希望對自己有用,對大家也有用吧。 2 執行力 重要的事情先說三遍! 計劃的本質是執行! 計劃的本質是執行! 計劃的本質是執行!
35所高校申報開設大資料專業,想學大資料相關專業看這裡!
從IT時代進入DT時代,高校在大資料方向上設定了哪些專業,具體學什麼,就業怎麼樣,作為新興專業,考生如何報考? 具體內容 專業名稱:資料科學與大資料技術; 人才培養目標:以大資料為核心研究物件,利用大資料的方法解決具體行業應用問題。 學制:四年;學位:工學或理學學位。 推薦
不想早起上班?餘額就是起床動力!
現在天氣是越來越冷了,你早上還起得來嗎?每天早上鬧鐘響了也要再躺一會才能爬起來的是不是你本人? 雖然大家都不想早起,但是班還是要上的,不然就只能吃土了。
程式收藏不看系列:一文輕鬆搞定系統限流
1. 我們為什麼需要限流 為了“反脆弱”,在微服務複雜拓撲的情況下,限流是保障服務彈性和拓撲健壯的重中之重。 想一想,如果業務推出了一個秒殺活動,而你沒有任何的限流措施;當你搭建了一個賬號平臺,而完全沒有對十幾個業務方設定流量配額……這些很有可能在特定場合下給你的產品帶來大量的業務損失和口碑影響。 我們通
看完讓你徹底搞懂Websocket原理
找到 說了 成了 原理 兩層 cep 告訴 edi 純粹 偶然在知乎上看到一篇回帖,瞬間覺得之前看的那麽多資料都不及這一篇回帖讓我對 websocket 的認識深刻有木有。所以轉到我博客裏,分享一下。比較喜歡看這種博客,讀起來很輕松,不枯燥,沒有布道師的陣仗,純粹為分享。廢
別再關心AI怎麽好了,我們要搞懂AI怎麽用
ant 坐標 rgb weight 部分 基本 安利 世界 ali 在今天的中國,想要了解AI,最怕的是什麽?答案也許只有一個,那就是開會。如今的人工智能大會基本是有套路的:首先要提問AI到底會不會取代人類;然後各個專家發言,暢想一下AI的美好未來;最後炫一張復雜的PPT或