python unicode 和string那📺
開發過程中總是會碰到string, unicode, ASCII, 中文字符等編碼的問題, 每次碰到都要現搜, 很是浪費時間, 於是這次狠下心, 一定要搞清楚python 的string和Unicode到底怎么回事.
基礎知識
我們都知道計算機只認0和1, 要想在計算機顯示26個字母, 就要給他們一套映射規則: 計算機能認得的符號 --> 人類可讀的符號. 這轉換的過程就是一套編碼規則.
- 字符集: 就是一套字符的集合(比如中文4000個漢字集合)
- 字符編碼: 一套法則, 能夠將0/1和人類的語言之間進行轉換的法則
最初字符集比較少, ASCII 碼就夠用了(一些控制符和26個字母), 隨着計算機的發展, 各國語言都有自己獨特的編碼, 漢字的編碼也不斷地擴展, 從GBK到 GB18030/DBCS. 這個時候Unicode應運而生.
Unicode就是為了統一各國各地區的編碼規則, 重新搞了一套包羅地球上所有文化, 符號的字符集! Unicode沒有編碼規則, 只是一套包含全世界符號的字符集. Unicode也不完美, 於是后續有了眾多UTF編碼(UTF-8, UTF-16).
總之搞清楚一件事情, 一個字符用了UTF-8編碼的, 就要用UTF-8去解碼, 不然就會出現亂碼.
文本處理
在python-2.x, 處理文本時, 有string和unicode兩種類型
- str類型就是一串bytes, 這種類型跟C語言中處理string是非常相似的
- unicode就是一串unicode的數字映射(code point), 用於映射某個字符與一個unicode的對應關系.
看看代碼出來是如何的:
>>> 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數字, 通過print a
和 print u
可以顯示出中文字符.
常見問題#1
大家經常犯的一個錯誤就是混淆了unicode以及通過unicode編碼存儲在string里面的類型.
比如上面的例子中 u'\u7b80'
是unicode, '\xe7\xae\x80'
是byte string, byte和unicode之間一一對應, 可以相互轉換, 轉換規則如下:
>>> '\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') 簡
總結一下, 上面例子中
- unicode和byte都指
簡
- byte string 里面存儲的是unicode通過utf-8編碼后得到的bytes
- 所以byte string解碼(decode)后即可得到unicode
- unicode是byte string通過utf-8解碼后得到的
- unicode用utf-8編碼(encode)可以得到對應的bytes
Note: 總而言之 Unicode ------編碼------> byte string Unicode <-----解碼------- byte string
Unicode就像是加密傳輸中的明文, 可以用UTF-8, UTF-16, UTF-7, UTF-32等對unicode進行加密, 最后解密還是要用回原本的加密方式來解密, 不然就解出亂碼啦.
常見問題#2
對unicode或者byte string編碼解碼方向搞錯
>>> 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應該是進行編碼的, 如果進行decode, 是會出現UnicodeEncodeError
異常的. bytes string同理, 應該進行解碼, 如果硬要進行編碼的話, 則拋出UnicodeDecodeError
常見問題#3
API調用不一致的問題. 在調用別人的API的時候, 需要看清楚是傳unicode還是byte string作為參數. 因為第三方的API有的是支持unicode, 有的是byte string, 甚至有的兩種類型都支持. 這個時候要清楚自己傳進去的參數是什么, 比如一些變量值是從http requests里面拉過來的, 這個時候你獲得的變量值很有可能是unicode類型(python requests get/post把返回值都轉成了unicode), 而如果第三方的API需要byte string, name就需要自己判斷一下並進行轉換. 否則就會出現各種奇怪的UnicodeError
雖然python 社區規定了在所有的API中使用unicode, 但是少數一部分的API處於安全考慮還是要求使用byte string. 需要注意一下.
常見問題#4
輸出類型不一致.
既然python社區推動到處使用unicode, 那么我們只要在開發過程中全部都轉成unicode是不是就萬事大吉了? 並不是, 當你要輸出文本到terminal或者到文件, 這個文本必須是byte string類型的.
如果不是的話, python會隱式地幫你將unicode轉成string, python默認采用ascii編碼,而中文編碼不在ascii編碼能夠表示的范圍之內,所以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)
所以當你需要輸出的時候, 需要將你的unicode轉換成byte string再寫文件, 如果有中文的話, 要用'utf-8'或'GBK'等支持中文的編碼.
>>> string.encode('utf-8')
python 2.x的unicode & str其實搞清楚之后來來回回就是那些小問題, 希望對大家有幫助.
================================================================================================================================================
一、
在爬蟲抓取網頁信息時常需要將類似"\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8"轉換為中文,實際上這是unicode的中文編碼。可用以下方法轉換:
1、
1 >>> s = u'\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8'
2 >>> print s
3 人生苦短,py是岸
2、
1 >>> s = r'\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8'
2 >>> s = s.decode('unicode_escape')
3 >>> print s
4 人生苦短,py是岸
二、
另外,在python2的字符編碼問題時常會遇到“UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)”的編碼錯誤。
而用以下方法通常可以解決:
1 import sys
2 reload(sys)
3 sys.setdefaultencoding('utf-8')
此方法是將Python2的默認編碼ASCII改為 utf-8。但此方法不是一勞永逸的,可能會使一些代碼的行為變得怪異。