Python 普通str字符串 和 unicode 字符串 及字符串編碼探測、轉換

本文研究時的環境是CentOS release 6.4,內核版本2.6.32-358.el6.x86_64,python2.6.6

內容:關於字符串的兩個魔術方法__str__() 、__unicode__() 兩個函數str() 、unicode() 類型轉換encode 、decode 和編碼探測chardet、 cchardethtml

先看一下對象的兩個魔術方法

第一個:object.__str__(self)python

Called by the str() built-in function and by the print statement to compute the 「informal」 string representation of an object.The return value must be a string object.shell

內建函數str()print語句 調用,產生非正式的對對象的描述字符串。返回值必須是string對象(這裏指的應該是bytes object字節對象)數組

第二個:object.__unicode__(self)網絡

Called to implement unicode() built-in; should return a Unicode object. When this method is not defined,string conversion is attempted, and the result of string conversion is converted to Unicode using the system default encoding.app

被內建函數unicode()調用;應當返回一個Unicode對象。當沒有定義此方法時,將會嘗試字符串轉換,字符串轉換的結果是:使用系統默認編碼將其轉換爲Unicode string。ide

str() 和 unicode()

str(object='')函數

Return a string containing a nicely printable representation of an object. For strings, this returns the string itself.If no argument is given, returns the empty string, ''.ui

返回對傳入對象的便於打印的描述的字符串(調用對象的__str__()方法)。對於字符串對象(字節對象)將會返回他自己。若是沒有參數,將返回空字符串。this

unicode(object='')

unicode(object[, encoding[, errors]])

If no optional parameters are given, unicode() will mimic the behaviour of str() except that it returns Unicode strings instead of 8-bit strings. More precisely, if object is a Unicode string or subclass it will return that Unicode string without any additional decoding applied.

若是沒有提供可選參數,unicode()將會模擬str()的行爲,只是返回的是Unicode strings而不是8-bit strings。更準確的狀況,若是傳入的對象是Unicode string或他的子類,將不會進行任何譯碼操做,直接返回它自己。

For objects which provide a __unicode__() method, it will call this method without arguments to create a Unicode string. For all other objects, the 8-bit string version or representation is requested and then converted to a Unicode string using the codec for the default encoding in 'strict' mode.

對於提供了__unicode__()方法的對象,將不帶參數調用此方法。其餘狀況下傳入的必須是8-bit字符串描述,然後用編碼解碼器用系統默認編碼譯碼爲Unicode string。

If encoding and/or errors are given, unicode() will decode the object which can either be an 8-bit string or a character buffer using the codec for encoding. The encoding parameter is a string giving the name of an encoding; if the encoding is not known, LookupError is raised. Error handling is done according toerrors; this specifies the treatment of characters which are invalid in the input encoding. If errors is'strict' (the default), a ValueError is raised on errors, while a value of 'ignore' causes errors to be silently ignored, and a value of 'replace' causes the official Unicode replacement character, U+FFFD, to be used to replace input characters which cannot be decoded. See also the codecs module.

簡單的說就是將傳入的8-bit字串或緩衝區用指定的編碼解碼生成Unicode string,error參數用來指定沒法解碼時的處理方式。

小結:

str():調用對象的__str__()方法,產生8-bit string

unicode():調用對象的__unicode__()方法,返回Unicode string。如過對象沒有__unicode__()方法就調用__str__()方法生成8-bit string(傳入的若是就是8-bit string將省略這一步),而後對其用系統默認編碼解碼,生成Unicode string。

可見,咱們本身作的類,最好仍是提供__str__()和__unicode__()方法,對於寫日誌,debug等是頗有用的。

注:查看python系統默認編碼(上文中unicode()將會使用的默認值)

import sys
print sys.getdefaultencoding()

在交互模式設置字符編碼

>>> reload(sys) # 這個很重要,不然報錯
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf8')
>>> sys.getdefaultencoding()
'utf8'

 

普通string和Unicode string的區別

首先確認系統環境設置的是UTF-8:
echo $LANG
en_US.UTF-8

而後確認使用的終端調成utf-8編碼不然有些實驗會混亂。

1.普通string能夠理解爲咱們平時理解的字符串,一個緩衝區裏邊放着字符串,內容多是各類類型的編碼。(我是把它看成C語言char型數組理解的)

>>> str_string = '你好,字符編碼'

str_string的內容多是:

utf8:   '\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe7\xbc\x96\xe7\xa0\x81'

utf16:   '\xff\xfe`O}Y\x0c\xff\x16\x7f\x01x'

utf32:   '\xff\xfe\x00\x00`O\x00\x00}Y\x00\x00\x0c\xff\x00\x00\x16\x7f\x00\x00\x01x\x00\x00'

gb2312: '\xc4\xe3\xba\xc3\xa3\xac\xb1\xe0\xc2\xeb'

2. unicode是一種對象,用unicode字符集保存字符串(內部存儲的是UCS2或UCS4,更底層依據操做系統的環境,是用wchar_t、unsigned short或unsigned long。詳情可在python文檔中搜索Encodings and Unicode及Py_UNICODE,

 

>>> unicode_string = u'你好,字符編碼'
>>> unicode_string
u'\u4f60\u4f1a\u597d\uff0c\u5b57\u7b26\u7f16\u7801'

下面這種狀況(unicode應該是u'裏邊是\uxxxx的格式,但這個倒是\x的),應該是系統編碼與終端(本人用的Xshell)編碼不一致形成,其實u'\xe4' == u'\u00e4',這樣容易形成亂碼,請避免這種環境。

>>> unicode_string = u'你好,字符編碼'
>>> unicode_string
u'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe5\xad\x97\xe7\xac\xa6\xe7\xbc\x96\xe7\xa0\x81'

參考http://stackoverflow.com/questions/9845842/bytes-in-a-unicode-python-string

小結:

普通字符串(8-bit string,字節字符串):

是用挨着的一個一個8位的二進制位保存字符串,是有編碼的區別的。

Unicode string:

是用UCS2(或UCS4編譯時決定)保存字符串的對象,是沒有編碼區別,用它能夠生成各類編碼的普通字符串。

encode和decode的使用

上邊兩種字符串的區別弄明白了這兩個函數就好理解了

encode是編碼,decode是解碼。

Unicode字符串要變成普通字符串就要用某種編碼去「編碼」(encode)。

普通字符串須要知道它自己是什麼編碼的,用此編碼來「解碼」(decode)才能生成Unicode字符串對象。

因此:

encode的使用

>>> a = u'你好,世界!'
>>> a
u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'
>>> a.encode('utf8')
'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c\xef\xbc\x81'
>>> a.encode('utf16')
'\xff\xfe`O}Y\x0c\xff\x16NLu\x01\xff'
>>> a.encode('gb2312')
'\xc4\xe3\xba\xc3\xa3\xac\xca\xc0\xbd\xe7\xa3\xa1'
>>> a.encode('ascii') # 由於ascii字符集沒法表示中文,因此會報錯,字符串是u'Hello,World!'就好了
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
>>>

decode的使用

>>> b = a.encode('utf8')#先用a生成某中編碼的普通字符串,而後進行decode,注意編碼必須對應!
>>> b.decode('utf8')
u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'
>>> b = a.encode('utf16')
>>> b.decode('utf16')
u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'
>>> b = a.encode('gb2312')
>>> b.decode('gb2312')
u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'
>>> b = a.encode('utf8')
>>> b.decode('gb2312')#編碼不對應的狀況,b是utf8編碼的字符串,用gb2312是不能解碼的。
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'gb2312' codec can't decode bytes in position 2-3: illegal multibyte sequence
>>> b.decode('gb2312','ignore')#這時候第二個參數出場了,能夠設置忽略或者用問號替換,防止拋出異常
u'\u6d63\u30bd\u951b'
>>> print b.decode('gb2312','ignore')
浣ソ錛
>>> b.decode('gb2312','replace')
u'\u6d63\ufffd\u30bd\u951b\ufffd\ufffd\ufffd\ufffd\ufffd'
>>> print b.decode('gb2312','replace')
浣�ソ錛�����
>>>

來個混合使用的:-)

>>> a = u'你好,世界!'
>>> a
u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'
>>> a.encode('utf8').decode('utf8')
u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'
>>> a.encode('utf8').decode('utf8').encode('gb2312')
'\xc4\xe3\xba\xc3\xa3\xac\xca\xc0\xbd\xe7\xa3\xa1'
>>> a.encode('utf8').decode('utf8').encode('gb2312').decode('gb2312')
u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'
>>> a.encode('utf8').decode('utf8').encode('gb2312').decode('gb2312').encode('utf16')
'\xff\xfe`O}Y\x0c\xff\x16NLu\x01\xff'
>>> a.encode('utf8').decode('utf8').encode('gb2312').decode('gb2312').encode('utf16').decode('utf16')u'\u4f60\u597d\uff0c\u4e16\u754c\uff01'

 

python普通字符串編碼檢測

普通字符串有編碼的分別,咱們常常遇到經過網絡或打開某個文件讀取字符串的狀況,而若是對端不是咱們本身的程序,用的什麼編碼還真很差說,這就涉及到字符串編碼檢測了。

提早聲明:

1.理論上是沒法100%檢測出是什麼編碼的,由於各類編碼間存在衝突,同一個編碼可能不一樣的字符集裏都出現了,可是表明不一樣的字符,這裏只能說檢測字符串最多是什麼編碼。(好比雖然你是用utf8編碼的‘abc’,可是會被探測爲ascii,由於ascii表示a、b、c的編碼和utf8同樣,這是utf8對ascii的兼容特性,其餘各類編碼不少也有這種兼容ascii的設計,這就是全屏亂碼,字母數字下劃線不會亂的緣由所在,由於用任何編碼解碼都能正確顯示abc123)

2.只能探測出「測碼庫」支持的編碼,不支持的編碼就無能爲力了。(設想你本身制定的私有編碼,別人怎麼能檢測出?)

3.被探測的樣本字符串字符數越多越準確,太少了不行,兩三個漢字的gb2312字符串是不能正確檢測出的。

言歸正傳,我們開始解碼!!

先去pypi下載檢測字符編碼的庫chardet或cchardet(文檔說它更快,可是依賴另外的一個庫)

https://pypi.python.org/pypi?%3Aaction=search&term=chardet&submit=search

 來自chardet文檔的例子:

The easiest way to use the Universal Encoding Detector library is with the detect function.

>>> import urllib
>>> rawdata = urllib.urlopen('http://yahoo.co.jp/').read()
>>> import chardet
>>> chardet.detect(rawdata)
{'encoding': 'EUC-JP', 'confidence': 0.99}

 

 

更高級的例子:

If you’re dealing with a large amount of text, you can call the Universal Encoding Detector library incrementally, and it will stop as soon as it is confident enough to report its results.

import urllib
from chardet.universaldetector import UniversalDetector

usock = urllib.urlopen('http://yahoo.co.jp/')
detector = UniversalDetector()
for line in usock.readlines():
    detector.feed(line)
    if detector.done: break
detector.close()
usock.close()
print detector.result
{'encoding': 'EUC-JP', 'confidence': 0.99}

 

 

還有一個:

If you want to detect the encoding of multiple texts (such as separate files)

import glob
from chardet.universaldetector import UniversalDetector

detector = UniversalDetector()
for filename in glob.glob('*.xml'):
    print filename.ljust(60),
    detector.reset()
    for line in file(filename, 'rb'):
        detector.feed(line)
        if detector.done: break
    detector.close()
    print detector.result

 

 

附:一篇比較好的關於python編碼的文章,英文的但很易懂。

Making Sense of Python Unicode

相關文章
相關標籤/搜索