python unicode 和string那些事

開發(fā)過程中總是會碰到string, unicode, ASCII, 中文字符等編碼的問題, 每次碰到都要現(xiàn)搜, 很是浪費(fèi)時(shí)間, 于是這次狠下心, 一定要搞清楚python 的string和Unicode到底怎么回事.

基礎(chǔ)知識

我們都知道計(jì)算機(jī)只認(rèn)0和1, 要想在計(jì)算機(jī)顯示26個(gè)字母, 就要給他們一套映射規(guī)則: 計(jì)算機(jī)能認(rèn)得的符號 --> 人類可讀的符號. 這轉(zhuǎn)換的過程就是一套編碼規(guī)則.

  • 字符集: 就是一套字符的集合(比如中文4000個(gè)漢字集合)
  • 字符編碼: 一套法則, 能夠?qū)?/1和人類的語言之間進(jìn)行轉(zhuǎn)換的法則

最初字符集比較少, ASCII 碼就夠用了(一些控制符和26個(gè)字母), 隨著計(jì)算機(jī)的發(fā)展, 各國語言都有自己獨(dú)特的編碼, 漢字的編碼也不斷地?cái)U(kuò)展, 從GBK到 GB18030/DBCS. 這個(gè)時(shí)候Unicode應(yīng)運(yùn)而生.

Unicode就是為了統(tǒng)一各國各地區(qū)的編碼規(guī)則, 重新搞了一套包羅地球上所有文化, 符號的字符集! Unicode沒有編碼規(guī)則, 只是一套包含全世界符號的字符集. Unicode也不完美, 于是后續(xù)有了眾多UTF編碼(UTF-8, UTF-16).

總之搞清楚一件事情, 一個(gè)字符用了UTF-8編碼的, 就要用UTF-8去解碼, 不然就會出現(xiàn)亂碼.


文本處理

在python-2.x, 處理文本時(shí), 有string和unicode兩種類型

  • str類型就是一串bytes, 這種類型跟C語言中處理string是非常相似的
  • unicode就是一串unicode的數(shù)字映射(code point), 用于映射某個(gè)字符與一個(gè)unicode的對應(yīng)關(guān)系.

看看代碼出來是如何的:

>>> a = "簡書"
>>> type(a)
<type 'str'>
>>> a
'\xe7\xae\x80\xe4\xb9\xa6'
>>> print a
簡書

>>> u = u"簡書"
>>> type(u)
<type 'unicode'>
>>> u
u'\u7b80\u4e66'
>>> print u
簡書

從上面的代碼可以看到, a = "簡書"是string類型, 可以看到a是一串 '\xe7\xae\x80\xe4\xb9\xa6' byte字符, 而u = u"簡書"是一串\uxxxx的unicode數(shù)字, 通過print aprint u可以顯示出中文字符.

常見問題#1

大家經(jīng)常犯的一個(gè)錯(cuò)誤就是混淆了unicode以及通過unicode編碼存儲在string里面的類型.
比如上面的例子中 u'\u7b80' 是unicode, '\xe7\xae\x80'是byte string, byte和unicode之間一一對應(yīng), 可以相互轉(zhuǎn)換, 轉(zhuǎn)換規(guī)則如下:

>>> '\xe7\xae\x80'.decode('utf-8')
u'\u7b80'
>>> print '\xe7\xae\x80'.decode('utf-8')
簡

>>> u'\u7b80'.encode('utf-8')
'\xe7\xae\x80'
>>> print u'\u7b80'.encode('utf-8')
簡

總結(jié)一下, 上面例子中

  • unicode和byte都指
  • byte string 里面存儲的是unicode通過utf-8編碼后得到的bytes
  • 所以byte string解碼(decode)后即可得到unicode
  • unicode是byte string通過utf-8解碼后得到的
  • unicode用utf-8編碼(encode)可以得到對應(yīng)的bytes
Note: 
總而言之 Unicode ------編碼------> byte string
        Unicode <-----解碼------- byte string

Unicode就像是加密傳輸中的明文, 可以用UTF-8, UTF-16, UTF-7, UTF-32等對unicode進(jìn)行加密, 最后解密還是要用回原本的加密方式來解密, 不然就解出亂碼啦.

常見問題#2

對unicode或者byte string編碼解碼方向搞錯(cuò)

>>> u'\u7b80'.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/serena/Documents/data-pipeline/data-ci-sqlbuffet-env/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u7b80' in position 0: ordinal not in range(128)

>>> '\xe7\xae\x80'.encode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 0: ordinal not in range(128)

unicode應(yīng)該是進(jìn)行編碼的, 如果進(jìn)行decode, 是會出現(xiàn)UnicodeEncodeError 異常的. bytes string同理, 應(yīng)該進(jìn)行解碼, 如果硬要進(jìn)行編碼的話, 則拋出UnicodeDecodeError

常見問題#3

API調(diào)用不一致的問題. 在調(diào)用別人的API的時(shí)候, 需要看清楚是傳unicode還是byte string作為參數(shù). 因?yàn)榈谌降腁PI有的是支持unicode, 有的是byte string, 甚至有的兩種類型都支持. 這個(gè)時(shí)候要清楚自己傳進(jìn)去的參數(shù)是什么, 比如一些變量值是從http requests里面拉過來的, 這個(gè)時(shí)候你獲得的變量值很有可能是unicode類型(python requests get/post把返回值都轉(zhuǎn)成了unicode), 而如果第三方的API需要byte string, name就需要自己判斷一下并進(jìn)行轉(zhuǎn)換. 否則就會出現(xiàn)各種奇怪的UnicodeError

雖然python 社區(qū)規(guī)定了在所有的API中使用unicode, 但是少數(shù)一部分的API處于安全考慮還是要求使用byte string. 需要注意一下.

常見問題#4

輸出類型不一致.
既然python社區(qū)推動(dòng)到處使用unicode, 那么我們只要在開發(fā)過程中全部都轉(zhuǎn)成unicode是不是就萬事大吉了? 并不是, 當(dāng)你要輸出文本到terminal或者到文件, 這個(gè)文本必須是byte string類型的.
如果不是的話, python會隱式地幫你將unicode轉(zhuǎn)成string, python默認(rèn)采用ascii編碼,而中文編碼不在ascii編碼能夠表示的范圍之內(nèi),所以string無法將“你好”作為ascii編碼保存為str類型淹真。

>>> string = unicode('你好', 'utf8')
>>> print string
你好
>>> log = open('/var/tmp/debug.log', 'w')
>>> log.write(string)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

所以當(dāng)你需要輸出的時(shí)候, 需要將你的unicode轉(zhuǎn)換成byte string再寫文件, 如果有中文的話, 要用'utf-8'或'GBK'等支持中文的編碼.

>>> string.encode('utf-8')

python 2.x的unicode & str其實(shí)搞清楚之后來來回回就是那些小問題, 希望對大家有幫助.

Reference

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市虱岂,隨后出現(xiàn)的幾起案子悼凑,更是在濱河造成了極大的恐慌,老刑警劉巖隙券,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牙肝,死亡現(xiàn)場離奇詭異唉俗,居然都是意外死亡嗤朴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門互躬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來播赁,“玉大人,你說我怎么就攤上這事吼渡∪菸” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵寺酪,是天一觀的道長坎背。 經(jīng)常有香客問我,道長寄雀,這世上最難降的妖魔是什么得滤? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮盒犹,結(jié)果婚禮上懂更,老公的妹妹穿的比我還像新娘。我一直安慰自己急膀,他們只是感情好沮协,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卓嫂,像睡著了一般慷暂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晨雳,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天行瑞,我揣著相機(jī)與錄音,去河邊找鬼餐禁。 笑死血久,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帮非。 我是一名探鬼主播洋魂,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喜鼓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衔肢,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤庄岖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后角骤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體隅忿,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡心剥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了背桐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片优烧。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖链峭,靈堂內(nèi)的尸體忽然破棺而出畦娄,到底是詐尸還是另有隱情,我是刑警寧澤弊仪,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布熙卡,位于F島的核電站,受9級特大地震影響励饵,放射性物質(zhì)發(fā)生泄漏驳癌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一役听、第九天 我趴在偏房一處隱蔽的房頂上張望颓鲜。 院中可真熱鬧,春花似錦典予、人聲如沸甜滨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽艳吠。三九已至,卻和暖如春孽椰,著一層夾襖步出監(jiān)牢的瞬間昭娩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工黍匾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栏渺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓锐涯,卻偏偏與公主長得像磕诊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子纹腌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容