python unicode 和string那些事

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

基礎(chǔ)知識(shí)

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

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

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

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

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


文本處理

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

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

看看代碼出來(lái)是如何的:

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

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

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

常見(jiàn)問(wèn)題#1

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

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

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

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

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

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

常見(jiàn)問(wèn)題#2

對(duì)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, 是會(huì)出現(xiàn)UnicodeEncodeError 異常的. bytes string同理, 應(yīng)該進(jìn)行解碼, 如果硬要進(jìn)行編碼的話, 則拋出UnicodeDecodeError

常見(jiàn)問(wèn)題#3

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

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

常見(jiàn)問(wèn)題#4

輸出類型不一致.
既然python社區(qū)推動(dòng)到處使用unicode, 那么我們只要在開發(fā)過(guò)程中全部都轉(zhuǎn)成unicode是不是就萬(wàn)事大吉了? 并不是, 當(dāng)你要輸出文本到terminal或者到文件, 這個(gè)文本必須是byte string類型的.
如果不是的話, python會(huì)隱式地幫你將unicode轉(zhuǎn)成string, python默認(rèn)采用ascii編碼,而中文編碼不在ascii編碼能夠表示的范圍之內(nèi),所以string無(wú)法將“你好”作為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í)搞清楚之后來(lái)來(lái)回回就是那些小問(wèn)題, 希望對(duì)大家有幫助.

Reference

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容