欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Python2.x中對Unicode編碼的使用

 更新時間:2015年04月03日 09:30:45   作者:ERIC MORITZ  
這篇文章主要介紹了詳解Python2.x中對Unicode編碼的使用,Python3中Unicode被作為默認(rèn)的編碼來使用,而在目前仍被廣泛應(yīng)用的Python2的版本中Unicode卻是一個在使用中需要注意的地方,需要的朋友可以參考下

我確定有很多關(guān)于Unicode和Python的說明,但為了方便自己的理解使用,我還是打算再寫一些關(guān)于它們的東西。

 
字節(jié)流 vs Unicode對象

我們先來用Python定義一個字符串。當(dāng)你使用string類型時,實(shí)際上會儲存一個字節(jié)串。
 

[ a ][ b ][ c ] = "abc"
[ 97 ][ 98 ][ 99 ] = "abc"

在這個例子里,abc這個字符串是一個字節(jié)串。97.,98,,99是ASCII碼。Python 2.x版本的一個不足之處就是默認(rèn)將所有的字符串當(dāng)做ASCII來對待。不幸的是,ASCII在拉丁式字符集里是最不常見的標(biāo)準(zhǔn)。

ASCII是用前127個數(shù)字來做字符映射。像windows-1252和UTF-8這樣的字符映射有相同的前127個字符。在你的字符串里每個字節(jié)的值低于127的時候是安全的混合字符串編碼。然而作這個假設(shè)是件很危險的事情,下面還將會提到。

當(dāng)你的字符串里有字節(jié)的值大于126的時候就會出現(xiàn)問題了。我們來看一個用windows-1252編碼的字符串。Windows-1252里的字符映射是8位的字符映射,那么總共就會有256個字符。前127個跟ASCII是一樣的,接下來的127個是由windows-1252定義的其他字符。
 

A windows-1252 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 150 ] = "abc–"

Windows-1252仍然是一個字節(jié)串,但你有沒有看到最后一個字節(jié)的值是大于126的。如果Python試著用默認(rèn)的ASCII標(biāo)準(zhǔn)來解碼這個字節(jié)流,它就會報錯。我們來看當(dāng)Python解碼這個字符串的時候會發(fā)生什么:
 

>>> x = "abc" + chr(150)
>>> print repr(x)
'abc\x96'
>>> u"Hello" + x
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
UnicodeDecodeError: 'ASCII' codec can't decode byte 0x96 in position 3: ordinal not in range(128)

我們來用UTF-8來編碼另一個字符串:
 

A UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"
[0x61] [0x62] [0x63] [0xe2] [ 0x80] [ 0x93] = "abc-"

如果你拿起看你熟悉的Unicode編碼表,你會發(fā)現(xiàn)英文的破折號對應(yīng)的Unicode編碼點(diǎn)為8211(0×2013)。這個值大于ASCII最大值127。大于一個字節(jié)能夠存儲的值。因?yàn)?211(0×2013)是兩個字節(jié),UTF-8必須利用一些技巧告訴系統(tǒng)存儲一個字符需要三個字節(jié)。我們再來看當(dāng)Python準(zhǔn)備用默認(rèn)的ASCII來編碼一個里面有字符的值大于126的UTF-8編碼字符串。
 

>>> x = "abc\xe2\x80\x93"
>>> print repr(x)
'abc\xe2\x80\x93'
>>> u"Hello" + x
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
UnicodeDecodeError: 'ASCII' codec can't decode byte 0xe2 in position 3: ordinal not in range(128)

你可以看到,Python一直是默認(rèn)使用ASCII編碼。當(dāng)它處理第4個字符的時候,因?yàn)樗闹禐?26大于126,所以Python拋出了錯誤。這就是混合編碼所帶來的問題。

 
解碼字節(jié)流

在一開始學(xué)習(xí)Python Unicode 的時候,解碼這個術(shù)語可能會讓人很疑惑。你可以把字節(jié)流解碼成一個Unicode對象,把一個Unicode 對象編碼為字節(jié)流。

Python需要知道如何將字節(jié)流解碼為Unicode對象。當(dāng)你拿到一個字節(jié)流,你調(diào)用它的“解碼方法來從它創(chuàng)建出一個Unicode對象。

你最好是盡早的將字節(jié)流解碼為Unicode。
 

>>> x = "abc\xe2\x80\x93"
>>> x = x.decode("utf-8")
>>> print type(x)
<type 'unicode'>
>>> y = "abc" + chr(150)
>>> y = y.decode("windows-1252")
>>> print type(y)
>>> print x + y
abc–abc–

 
將Unicode編碼為字節(jié)流

Unicode對象是一個文本的編碼不可知論的代表。你不能簡單地輸出一個Unicode對象。它必須在輸出前被變成一個字節(jié)串。Python會很適合做這樣的工作,盡管Python將Unicode編碼為字節(jié)流時默認(rèn)是適用ASCII,這個默認(rèn)的行為會成為很多讓人頭疼的問題的原因。
 

>>> u = u"abc\u2013"
>>> print u
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2013' in position 3: ordinal not in range(128)
>>> print u.encode("utf-8")
abc–

 
使用codecs模塊

codecs模塊能在處理字節(jié)流的時候提供很大幫助。你可以用定義的編碼來打開文件并且你從文件里讀取的內(nèi)容會被自動轉(zhuǎn)化為Unicode對象。

試試這個:
 

>>> import codecs
>>> fh = codecs.open("/tmp/utf-8.txt", "w", "utf-8")
>>> fh.write(u"\u2013")
>>> fh.close()

它所做的就是拿到一個Unicode對象然后將它以utf-8編碼寫入到文件。你也可以在其他的情況下這么使用它。

試試這個:

當(dāng)從一個文件讀取數(shù)據(jù)的時候,codecs.open 會創(chuàng)建一個文件對象能夠自動將utf-8編碼文件轉(zhuǎn)化為一個Unicode對象。

我們接著上面的例子,這次使用urllib流。
 

>>> stream = urllib.urlopen("http://www.google.com")
>>> Reader = codecs.getreader("utf-8")
>>> fh = Reader(stream)
>>> type(fh.read(1))
<type 'unicode'>
>>> Reader
<class encodings.utf_8.StreamReader at 0xa6f890>

單行版本:
 

>>> fh = codecs.getreader("utf-8")(urllib.urlopen("http://www.google.com"))
>>> type(fh.read(1))

你必須對codecs模塊十分小心。你傳進(jìn)去的東西必須是一個Unicode對象,否則它會自動將字節(jié)流作為ASCII進(jìn)行解碼。
 

>>> x = "abc\xe2\x80\x93" # our "abc-" utf-8 string
>>> fh = codecs.open("/tmp/foo.txt", "w", "utf-8")
>>> fh.write(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.5/codecs.py", line 638, in write
 return self.writer.write(data)
File "/usr/lib/python2.5/codecs.py", line 303, in write
 data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 3: ordinal not in range(128)

哎呦我去,Python又開始用ASCII來解碼一切了。
將UTF-8字節(jié)流切片的問題

因?yàn)橐粋€UTF-8編碼串是一個字節(jié)列表,len( )和切片操作無法正常工作。首先用我們之前用的字符串。
 

[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"

接下來做以下的:
 

>>> my_utf8 = "abc–"
>>> print len(my_utf8)
6

神馬?它看起來是4個字符,但是len的結(jié)果說是6。因?yàn)閘en計算的是字節(jié)數(shù)而不是字符數(shù)。
 

>>> print repr(my_utf8)
'abc\xe2\x80\x93'

現(xiàn)在我們來切分這個字符串。
 

>>> my_utf8[-1] # Get the last char
'\x93'

我去,切分結(jié)果是最后一字節(jié),不是最后一個字符。

為了正確的切分UTF-8,你最好是解碼字節(jié)流創(chuàng)建一個Unicode對象。然后就能安全的操作和計數(shù)了。
 

>>> my_unicode = my_utf8.decode("utf-8")
>>> print repr(my_unicode)
u'abc\u2013'
>>> print len(my_unicode)
4
>>> print my_unicode[-1]
–

 
當(dāng)Python自動地編碼/解碼

在一些情況下,當(dāng)Python自動地使用ASCII進(jìn)行編碼/解碼的時候會拋出錯誤。

第一個案例是當(dāng)它試著將Unicode和字節(jié)串合并在一起的時候。
 

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

在合并列表的時候會發(fā)生同樣的情況。Python在列表里有string和Unicode對象的時候會自動地將字節(jié)串解碼為Unicode。
 

>>> ",".join([u"This string\u2019s unicode", u"This string\u2019s utf-8".encode("utf-8")])
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 11: ordinal not in range(128)

或者當(dāng)試著格式化一個字節(jié)串的時候:
 

>>> "%s\n%s" % (u"This string\u2019s unicode", u"This string\u2019s utf-8".encode("utf-8"),)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 11: ordinal not in range(128)

基本上當(dāng)你把Unicode和字節(jié)串混在一起用的時候,就會導(dǎo)致出錯。

在這個例子里面,你創(chuàng)建一個utf-8文件,然后往里面添加一些Unicode對象的文本。就會報UnicodeDecodeError錯誤。
 

>>> buffer = []
>>> fh = open("utf-8-sample.txt")
>>> buffer.append(fh.read())
>>> fh.close()
>>> buffer.append(u"This string\u2019s unicode")
>>> print repr(buffer)
['This file\xe2\x80\x99s got utf-8 in it\n', u'This string\u2019s unicode']
>>> print "\n".join(buffer)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 9: ordinal not in range(128)

你可以使用codecs模塊把文件作為Unicode加載來解決這個問題。
 

>>> import codecs
>>> buffer = []
>>> fh = open("utf-8-sample.txt", "r", "utf-8")
>>> buffer.append(fh.read())
>>> fh.close()
>>> print repr(buffer)
[u'This file\u2019s got utf-8 in it\n', u'This string\u2019s unicode']
>>> buffer.append(u"This string\u2019s unicode")
>>> print "\n".join(buffer)
This file's got utf-8 in it
 
This string's unicode

正如你看到的,由codecs.open 創(chuàng)建的流在當(dāng)數(shù)據(jù)被讀取的時候自動地將比特串轉(zhuǎn)化為Unicode。

 
最佳實(shí)踐

1.最先解碼,最后編碼

2.默認(rèn)使用utf-8編碼

3.使用codecs和Unicode對象來簡化處理

最先解碼意味著無論何時有字節(jié)流輸入,需要盡早將輸入解碼為Unicode。這會防止出現(xiàn)len( )和切分utf-8字節(jié)流發(fā)生問題。

最后編碼意味著只有你打算將文本輸出到某個地方時,才把它編碼為字節(jié)流。這個輸出可能是一個文件,一個數(shù)據(jù)庫,一個socket等等。只有在處理完成之后才編碼unicode對象。最后編碼也意味著,不要讓Python為你編碼Unicode對象。Python將會使用ASCII編碼,你的程序會崩潰。

默認(rèn)使用UTF-8編碼意味著:因?yàn)閁TF-8可以處理任何Unicode字符,所以你最好用它來替代windows-1252和ASCII。

codecs模塊能夠讓我們在處理諸如文件或socket這樣的流的時候能少踩一些坑。如果沒有codecs提供的這個工具,你就必須將文件內(nèi)容讀取為字節(jié)流,然后將這個字節(jié)流解碼為Unicode對象。

codecs模塊能夠讓你快速的將字節(jié)流轉(zhuǎn)化為Unicode對象,省去很多麻煩。

 
解釋UTF-8

最后的部分是讓你能對UTF-8有一個入門的了解,如果你是個超級極客可以無視這一段。

利用UTF-8,任何在127和255之間的字節(jié)是特別的。這些字節(jié)告訴系統(tǒng)這些字節(jié)是多字節(jié)序列的一部分。
 

Our UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"

最后3字節(jié)是一個UTF-8多字節(jié)序列。如果你把這三個字節(jié)里的第一個轉(zhuǎn)化為2進(jìn)制可以看到以下的結(jié)果:
 

11100010

前3比特告訴系統(tǒng)它開始了一個3字節(jié)序列226,128,147。

那么完整的字節(jié)序列。
 

11100010 10000000 10010011

然后你對三字節(jié)序列運(yùn)用下面的掩碼。(詳見這里

 
1110xxxx 10xxxxxx 10xxxxxx
XXXX0010 XX000000 XX010011 Remove the X's
0010    000000  010011 Collapse the numbers
00100000 00010011     Get Unicode number 0x2013, 8211 The "–"

這里僅僅是關(guān)于UTF-8的一些入門的基本知識,如果想知道更多的細(xì)節(jié),可以去看UTF-8的維基頁面。

相關(guān)文章

  • Python調(diào)用Tkinter示例淺析

    Python調(diào)用Tkinter示例淺析

    這篇文章主要介紹了Python調(diào)用Tkinter示例,通過在Python程序中設(shè)計按鈕,可以方便用戶調(diào)用Python程序,從而達(dá)到快速、自動化、高效的目的,提高用戶體驗(yàn)和工作效率
    2023-02-02
  • python相對企業(yè)語言優(yōu)勢在哪

    python相對企業(yè)語言優(yōu)勢在哪

    在本篇文章里小編給大家分享的是關(guān)于python相對企業(yè)語言優(yōu)勢以及相關(guān)知識點(diǎn),需要的朋友們可以參考下。
    2020-06-06
  • Python中方法的缺省參數(shù)問題解讀

    Python中方法的缺省參數(shù)問題解讀

    這篇文章主要介紹了Python中方法的缺省參數(shù)問題解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 用python生成(動態(tài)彩色)二維碼的方法(使用myqr庫實(shí)現(xiàn))

    用python生成(動態(tài)彩色)二維碼的方法(使用myqr庫實(shí)現(xiàn))

    今天小編就為大家分享一篇用python生成(動態(tài)彩色)二維碼的方法(使用myqr庫實(shí)現(xiàn)),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-06-06
  • python之excel文件(.xls文件)處理方式

    python之excel文件(.xls文件)處理方式

    這篇文章主要介紹了python之excel文件(.xls文件)處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Django框架實(shí)現(xiàn)的分頁demo示例

    Django框架實(shí)現(xiàn)的分頁demo示例

    這篇文章主要介紹了Django框架實(shí)現(xiàn)的分頁demo,結(jié)合實(shí)例形式分析了Django框架分頁的步驟、原理、相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2019-05-05
  • python os模塊和fnmatch模塊的使用介紹

    python os模塊和fnmatch模塊的使用介紹

    這篇文章主要介紹了python os模塊和fnmatch模塊的使用介紹,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下
    2021-03-03
  • python3中@dataclass的實(shí)現(xiàn)示例

    python3中@dataclass的實(shí)現(xiàn)示例

    @dataclass?是 Python 3.7 引入的一個裝飾器,用于方便地定義符合數(shù)據(jù)類協(xié)議的類,本文主要介紹了python3中@dataclass的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-02-02
  • 徹底理解Python中的yield關(guān)鍵字

    徹底理解Python中的yield關(guān)鍵字

    今天小編就為大家分享一篇關(guān)于徹底理解Python中的yield關(guān)鍵字,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • python檢測某個變量是否有定義的方法

    python檢測某個變量是否有定義的方法

    這篇文章主要介紹了python檢測某個變量是否有定義的方法,實(shí)例分析了Python常用的變量判定技巧,需要的朋友可以參考下
    2015-05-05

最新評論