亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

再談Python中的字符串與字符編碼(推薦)

 更新時(shí)間:2016年12月14日 11:26:40   作者:云游道士  
這篇文章主要介紹了再談Python中的字符串與字符編碼(推薦),具有一定的參考價(jià)值,有需要的可以了解一下。

本節(jié)內(nèi)容:

1.前言

2.相關(guān)概念

3.Python中的默認(rèn)編碼

4.Python2與Python3中對(duì)字符串的支持

5.字符編碼轉(zhuǎn)換

一、前言

Python中的字符編碼是個(gè)老生常談的話題,同行們都寫過(guò)很多這方面的文章。有的人云亦云,也有的寫得很深入。近日看到某知名培訓(xùn)機(jī)構(gòu)的教學(xué)視頻中再次談及此問(wèn)題,講解的還是不盡人意,所以才想寫這篇文字。一方面,梳理一下相關(guān)知識(shí),另一方面,希望給其他人些許幫助。

Python2的 默認(rèn)編碼 是ASCII,不能識(shí)別中文字符,需要顯式指定字符編碼;Python3的 默認(rèn)編碼 為Unicode,可以識(shí)別中文字符。

相信大家在很多文章中都看到過(guò)類似上面這樣“對(duì)Python中中文處理”的解釋,也相信大家在最初看到這樣的解釋的時(shí)候確實(shí)覺(jué)得明白了??墒菚r(shí)間久了之后,再重復(fù)遇到相關(guān)問(wèn)題就會(huì)覺(jué)得貌似理解的又不是那么清楚了。如果我們了解上面說(shuō)的默認(rèn)編碼的作用是什么,我們就會(huì)更清晰的明白那句話的含義。

二、相關(guān)概念

1. 字符與字節(jié)

一個(gè)字符不等價(jià)于一個(gè)字節(jié),字符是人類能夠識(shí)別的符號(hào),而這些符號(hào)要保存到計(jì)算的存儲(chǔ)中就需要用計(jì)算機(jī)能夠識(shí)別的字節(jié)來(lái)表示。一個(gè)字符往往有多種表示方法,不同的表示方法會(huì)使用不同的字節(jié)數(shù)。這里所說(shuō)的不同的表示方法就是指字符編碼,比如字母A-Z都可以用ASCII碼表示(占用一個(gè)字節(jié)),也可以用UNICODE表示(占兩個(gè)字節(jié)),還可以用UTF-8表示(占用一個(gè)字節(jié))。字符編碼的作用就是將人類可識(shí)別的字符轉(zhuǎn)換為機(jī)器可識(shí)別的字節(jié)碼,以及反向過(guò)程。

UNICDOE才是真正的字符串,而用ASCII、UTF-8、GBK等字符編碼表示的是字節(jié)串。關(guān)于這點(diǎn),我們可以在Python的官方文檔中經(jīng)??梢钥吹竭@樣的描述"Unicode string" , " translating a Unicode string into a sequence of bytes"

我們寫代碼是寫在文件中的,而字符是以字節(jié)形式保存在文件中的,因此當(dāng)我們?cè)谖募卸x個(gè)字符串時(shí)被當(dāng)做字節(jié)串也是可以理解的。但是,我們需要的是字符串,而不是字節(jié)串。一個(gè)優(yōu)秀的編程語(yǔ)言,應(yīng)該嚴(yán)格區(qū)分兩者的關(guān)系并提供巧妙的完美的支持。JAVA語(yǔ)言就很好,以至于了解Python和PHP之前我從來(lái)沒(méi)有考慮過(guò)這些不應(yīng)該由程序員來(lái)處理的問(wèn)題。遺憾的是,很多編程語(yǔ)言試圖混淆“字符串”和“字節(jié)串”,他們把字節(jié)串當(dāng)做字符串來(lái)使用,PHP和Python2都屬于這種編程語(yǔ)言。最能說(shuō)明這個(gè)問(wèn)題的操作就是取一個(gè)包含中文字符的字符串的長(zhǎng)度:

  • 對(duì)字符串取長(zhǎng)度,結(jié)果應(yīng)該是所有字符串的個(gè)數(shù),無(wú)論中文還是英文
  • 對(duì)字符串對(duì)應(yīng)的字節(jié)串取長(zhǎng)度,就跟編碼(encode)過(guò)程使用的字符編碼有關(guān)了(比如:UTF-8編碼,一個(gè)中文字符需要用3個(gè)字節(jié)來(lái)表示;GBK編碼,一個(gè)中文字符需要2個(gè)字節(jié)來(lái)表示)

注意:Windows的cmd終端字符編碼默認(rèn)為GBK,因此在cmd輸入的中文字符需要用兩個(gè)字節(jié)表示

>>> # Python2
>>> a = 'Hello,中國(guó)' # 字節(jié)串,長(zhǎng)度為字節(jié)個(gè)數(shù) = len('Hello,')+len('中國(guó)') = 6+2*2 = 10
>>> b = u'Hello,中國(guó)' # 字符串,長(zhǎng)度為字符個(gè)數(shù) = len('Hello,')+len('中國(guó)') = 6+2 = 8
>>> c = unicode(a, 'gbk') # 其實(shí)b的定義方式是c定義方式的簡(jiǎn)寫,都是將一個(gè)GBK編碼的字節(jié)串解碼(decode)為一個(gè)Uniocde字符串
>>> 
>>> print(type(a), len(a))
(<type 'str'>, 10)
>>> print(type(b), len(b))
(<type 'unicode'>, 8)
>>> print(type(c), len(c))
(<type 'unicode'>, 8)
>>>

Python3中對(duì)字符串的支持做了很大的改動(dòng),具體內(nèi)容會(huì)在下面介紹。

2. 編碼與解碼

先做下科普:UNICODE字符編碼,也是一張字符與數(shù)字的映射,但是這里的數(shù)字被稱為代碼點(diǎn)(code point), 實(shí)際上就是十六進(jìn)制的數(shù)字。

Python官方文檔中對(duì)Unicode字符串、字節(jié)串與編碼之間的關(guān)系有這樣一段描述:

Unicode字符串是一個(gè)代碼點(diǎn)(code point)序列,代碼點(diǎn)取值范圍為0到0x10FFFF(對(duì)應(yīng)的十進(jìn)制為1114111)。這個(gè)代碼點(diǎn)序列在存儲(chǔ)(包括內(nèi)存和物理磁盤)中需要被表示為一組字節(jié)(0到255之間的值),而將Unicode字符串轉(zhuǎn)換為字節(jié)序列的規(guī)則稱為編碼。

這里說(shuō)的編碼不是指字符編碼,而是指編碼的過(guò)程以及這個(gè)過(guò)程中所使用到的Unicode字符的代碼點(diǎn)與字節(jié)的映射規(guī)則。這個(gè)映射不必是簡(jiǎn)單的一對(duì)一映射,因此編碼過(guò)程也不必處理每個(gè)可能的Unicode字符,例如:

將Unicode字符串轉(zhuǎn)換為ASCII編碼的規(guī)則很簡(jiǎn)單--對(duì)于每個(gè)代碼點(diǎn):

  • 如果代碼點(diǎn)數(shù)值<128,則每個(gè)字節(jié)與代碼點(diǎn)的值相同
  • 如果代碼點(diǎn)數(shù)值>=128,則Unicode字符串無(wú)法在此編碼中進(jìn)行表示(這種情況下,Python會(huì)引發(fā)一個(gè)UnicodeEncodeError異常)

將Unicode字符串轉(zhuǎn)換為UTF-8編碼使用以下規(guī)則:

  • 如果代碼點(diǎn)數(shù)值<128,則由相應(yīng)的字節(jié)值表示(與Unicode轉(zhuǎn)ASCII字節(jié)一樣)
  • 如果代碼點(diǎn)數(shù)值>=128,則將其轉(zhuǎn)換為一個(gè)2個(gè)字節(jié),3個(gè)字節(jié)或4個(gè)字節(jié)的序列,該序列中的每個(gè)字節(jié)都在128到255之間。

簡(jiǎn)單總結(jié):

  • 編碼(encode):將Unicode字符串(中的代碼點(diǎn))轉(zhuǎn)換特定字符編碼對(duì)應(yīng)的字節(jié)串的過(guò)程和規(guī)則
  • 解碼(decode):將特定字符編碼的字節(jié)串轉(zhuǎn)換為對(duì)應(yīng)的Unicode字符串(中的代碼點(diǎn))的過(guò)程和規(guī)則

可見(jiàn),無(wú)論是編碼還是解碼,都需要一個(gè)重要因素,就是特定的字符編碼。因?yàn)橐粋€(gè)字符用不同的字符編碼進(jìn)行編碼后的字節(jié)值以及字節(jié)個(gè)數(shù)大部分情況下是不同的,反之亦然。

三、Python中的默認(rèn)編碼

1. Python源代碼文件的執(zhí)行過(guò)程

我們都知道,磁盤上的文件都是以二進(jìn)制格式存放的,其中文本文件都是以某種特定編碼的字節(jié)形式存放的。對(duì)于程序源代碼文件的字符編碼是由編輯器指定的,比如我們使用Pycharm來(lái)編寫Python程序時(shí)會(huì)指定工程編碼和文件編碼為UTF-8,那么Python代碼被保存到磁盤時(shí)就會(huì)被轉(zhuǎn)換為UTF-8編碼對(duì)應(yīng)的字節(jié)(encode過(guò)程)后寫入磁盤。當(dāng)執(zhí)行Python代碼文件中的代碼時(shí),Python解釋器在讀取Python代碼文件中的字節(jié)串之后,需要將其轉(zhuǎn)換為UNICODE字符串(decode過(guò)程)之后才執(zhí)行后續(xù)操作。

上面已經(jīng)解釋過(guò),這個(gè)轉(zhuǎn)換過(guò)程(decode,解碼)需要我們指定文件中保存的字節(jié)使用的字符編碼是什么,才能知道這些字節(jié)在UNICODE這張萬(wàn)國(guó)碼和統(tǒng)一碼中找到其對(duì)應(yīng)的代碼點(diǎn)是什么。這里指定字符編碼的方式大家都很熟悉,如下所示:

# -*- coding:utf-8 -*-

2. 默認(rèn)編碼

那么,如果我們沒(méi)有在代碼文件開始的部分指定字符編碼,Python解釋器就會(huì)使用哪種字符編碼把從代碼文件中讀取到的字節(jié)轉(zhuǎn)換為UNICODE代碼點(diǎn)呢?就像我們配置某些軟件時(shí),有很多默認(rèn)選項(xiàng)一樣,需要在Python解釋器內(nèi)部設(shè)置默認(rèn)的字符編碼來(lái)解決這個(gè)問(wèn)題,這就是文章開頭所說(shuō)的“默認(rèn)編碼”。因此大家所說(shuō)的Python中文字符問(wèn)題就可以總結(jié)為一句話:當(dāng)無(wú)法通過(guò)默認(rèn)的字符編碼對(duì)字節(jié)進(jìn)行轉(zhuǎn)換時(shí),就會(huì)出現(xiàn)解碼錯(cuò)誤(UnicodeEncodeError)。

Python2和Python3的解釋器使用的默認(rèn)編碼是不一樣的,我們可以通過(guò)sys.getdefaultencoding()來(lái)獲取默認(rèn)編碼:

>>> # Python2
>>> import sys
>>> sys.getdefaultencoding()
'ascii'

>>> # Python3
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'

因此,對(duì)于Python2來(lái)講,Python解釋器在讀取到中文字符的字節(jié)碼嘗試解碼操作時(shí),會(huì)先查看當(dāng)前代碼文件頭部是否有指明當(dāng)前代碼文件中保存的字節(jié)碼對(duì)應(yīng)的字符編碼是什么。如果沒(méi)有指定則使用默認(rèn)字符編碼"ASCII"進(jìn)行解碼導(dǎo)致解碼失敗,導(dǎo)致如下錯(cuò)誤:

SyntaxError: Non-ASCII character '\xc4' in file xxx.py on line 11, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

對(duì)于Python3來(lái)講,執(zhí)行過(guò)程是一樣的,只是Python3的解釋器以"UTF-8"作為默認(rèn)編碼,但是這并不表示可以完全兼容中文問(wèn)題。比如我們?cè)赪indows上進(jìn)行開發(fā)時(shí),Python工程及代碼文件都使用的是默認(rèn)的GBK編碼,也就是說(shuō)Python代碼文件是被轉(zhuǎn)換成GBK格式的字節(jié)碼保存到磁盤中的。Python3的解釋器執(zhí)行該代碼文件時(shí),試圖用UTF-8進(jìn)行解碼操作時(shí),同樣會(huì)解碼失敗,導(dǎo)致如下錯(cuò)誤:

SyntaxError: Non-UTF-8 code starting with '\xc4' in file xxx.py on line 11, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

3. 最佳實(shí)踐

  • 創(chuàng)建一個(gè)工程之后先確認(rèn)該工程的字符編碼是否已經(jīng)設(shè)置為UTF-8
  • 為了兼容Python2和Python3,在代碼頭部聲明字符編碼:-*- coding:utf-8 -*-

四、Python2與Python3中對(duì)字符串的支持

其實(shí)Python3中對(duì)字符串支持的改進(jìn),不僅僅是更改了默認(rèn)編碼,而是重新進(jìn)行了字符串的實(shí)現(xiàn),而且它已經(jīng)實(shí)現(xiàn)了對(duì)UNICODE的內(nèi)置支持,從這方面來(lái)講Python已經(jīng)和JAVA一樣優(yōu)秀。下面我們來(lái)看下Python2與Python3中對(duì)字符串的支持有什么區(qū)別:

Python2

Python2中對(duì)字符串的支持由以下三個(gè)類提供

class basestring(object)
  class str(basestring)
  class unicode(basestring)

執(zhí)行help(str)和help(bytes)會(huì)發(fā)現(xiàn)結(jié)果都是str類的定義,這也說(shuō)明Python2中str就是字節(jié)串,而后來(lái)的unicode對(duì)象對(duì)應(yīng)才是真正的字符串。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

a = '你好'
b = u'你好'

print(type(a), len(a))
print(type(b), len(b))

輸出結(jié)果:

(<type 'str'>, 6)

(<type 'unicode'>, 2)

Python3

Python3中對(duì)字符串的支持進(jìn)行了實(shí)現(xiàn)類層次的上簡(jiǎn)化,去掉了unicode類,添加了一個(gè)bytes類。從表面上來(lái)看,可以認(rèn)為Python3中的str和unicode合二為一了。

class bytes(object)
class str(object)

實(shí)際上,Python3中已經(jīng)意識(shí)到之前的錯(cuò)誤,開始明確的區(qū)分字符串與字節(jié)。因此Python3中的str已經(jīng)是真正的字符串,而字節(jié)是用單獨(dú)的bytes類來(lái)表示。也就是說(shuō),Python3默認(rèn)定義的就是字符串,實(shí)現(xiàn)了對(duì)UNICODE的內(nèi)置支持,減輕了程序員對(duì)字符串處理的負(fù)擔(dān)。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

a = '你好'
b = u'你好'
c = '你好'.encode('gbk')

print(type(a), len(a))
print(type(b), len(b))
print(type(c), len(c))

輸出結(jié)果:

<class 'str'> 2

<class 'str'> 2

<class 'bytes'> 4

五、字符編碼轉(zhuǎn)換

上面提到,UNICODE字符串可以與任意字符編碼的字節(jié)進(jìn)行相互轉(zhuǎn)換,如圖:

那么大家很容易想到一個(gè)問(wèn)題,就是不同的字符編碼的字節(jié)可以通過(guò)Unicode相互轉(zhuǎn)換嗎?答案是肯定的。

Python2中的字符串進(jìn)行字符編碼轉(zhuǎn)換過(guò)程是:

字節(jié)串-->decode('原來(lái)的字符編碼')-->Unicode字符串-->encode('新的字符編碼')-->字節(jié)串

#!/usr/bin/env python
# -*- coding:utf-8 -*-


utf_8_a = '我愛(ài)中國(guó)'
gbk_a = utf_8_a.decode('utf-8').encode('gbk')
print(gbk_a.decode('gbk'))

輸出結(jié)果:

我愛(ài)中國(guó)

Python3中定義的字符串默認(rèn)就是unicode,因此不需要先解碼,可以直接編碼成新的字符編碼:

字符串-->encode('新的字符編碼')-->字節(jié)串

#!/usr/bin/env python
# -*- coding:utf-8 -*-


utf_8_a = '我愛(ài)中國(guó)'
gbk_a = utf_8_a.encode('gbk')
print(gbk_a.decode('gbk'))

輸出結(jié)果:

我愛(ài)中國(guó)

最后需要說(shuō)明的是,Unicode不是有道詞典,也不是google翻譯器,它并不能把一個(gè)中文翻譯成一個(gè)英文。正確的字符編碼的轉(zhuǎn)換過(guò)程只是把同一個(gè)字符的字節(jié)表現(xiàn)形式改變了,而字符本身的符號(hào)是不應(yīng)該發(fā)生變化的,因此并不是所有的字符編碼之間的轉(zhuǎn)換都是有意義的。怎么理解這句話呢?比如GBK編碼的“中國(guó)”轉(zhuǎn)成UTF-8字符編碼后,僅僅是由4個(gè)字節(jié)變成了6個(gè)字節(jié)來(lái)表示,但其字符表現(xiàn)形式還應(yīng)該是“中國(guó)”,而不應(yīng)該變成“你好”或者“China”。

前面花了很大的篇幅介紹概念和理論,后面注重實(shí)踐,希望對(duì)他人有所幫助。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python實(shí)現(xiàn)使用request模塊下載圖片demo示例

    Python實(shí)現(xiàn)使用request模塊下載圖片demo示例

    這篇文章主要介紹了Python實(shí)現(xiàn)使用request模塊下載圖片,結(jié)合完整實(shí)例形式分析了Python基于requests模塊的流傳輸文件下載操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-05-05
  • python文件目錄操作之os模塊

    python文件目錄操作之os模塊

    這篇文章主要介紹了python文件目錄操作之os模塊,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-05-05
  • Python通過(guò)fnmatch模塊實(shí)現(xiàn)文件名匹配

    Python通過(guò)fnmatch模塊實(shí)現(xiàn)文件名匹配

    這篇文章主要介紹了Python通過(guò)fnmatch模塊實(shí)現(xiàn)文件名匹配,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 如何解決MNIST數(shù)據(jù)集下載速度較慢并失敗的問(wèn)題

    如何解決MNIST數(shù)據(jù)集下載速度較慢并失敗的問(wèn)題

    這篇文章主要介紹了如何解決MNIST數(shù)據(jù)集下載速度較慢并失敗的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Python中字符串List按照長(zhǎng)度排序

    Python中字符串List按照長(zhǎng)度排序

    這篇文章主要介紹了字符串List按照長(zhǎng)度排序(python)的實(shí)現(xiàn)方法啊,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Python中列表、字典、元組、集合數(shù)據(jù)結(jié)構(gòu)整理

    Python中列表、字典、元組、集合數(shù)據(jù)結(jié)構(gòu)整理

    這篇文章主要介紹了Python中列表、字典、元組、集合數(shù)據(jù)結(jié)構(gòu)整理,較為詳細(xì)的分析了這幾類數(shù)據(jù)結(jié)構(gòu)的具體用法及相關(guān)技巧,需要的朋友可以參考下
    2014-11-11
  • Python使用Selenium爬取淘寶異步加載的數(shù)據(jù)方法

    Python使用Selenium爬取淘寶異步加載的數(shù)據(jù)方法

    今天小編就為大家分享一篇Python使用Selenium爬取淘寶異步加載的數(shù)據(jù)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • Python實(shí)現(xiàn)PDF頁(yè)面的刪除與添加功能

    Python實(shí)現(xiàn)PDF頁(yè)面的刪除與添加功能

    在處理PDF文檔的過(guò)程中,我們時(shí)常會(huì)需要對(duì)PDF文檔中的頁(yè)面進(jìn)行編輯操作的情況,如插入和刪除頁(yè)面,通過(guò)添加和刪除PDF頁(yè)面,我們可以增加內(nèi)容或?qū)Σ恍枰膬?nèi)容進(jìn)行刪除,本文將介紹如何使用Python代碼實(shí)現(xiàn)在PDF文檔中添加和刪除頁(yè)面
    2024-04-04
  • Flask?+?MySQL如何實(shí)現(xiàn)用戶注冊(cè),登錄和登出的項(xiàng)目實(shí)踐

    Flask?+?MySQL如何實(shí)現(xiàn)用戶注冊(cè),登錄和登出的項(xiàng)目實(shí)踐

    本文主要介紹了Flask?+?MySQL?如何實(shí)現(xiàn)用戶注冊(cè),登錄和登出的項(xiàng)目實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Python讀取Hive數(shù)據(jù)庫(kù)實(shí)現(xiàn)代碼詳解

    Python讀取Hive數(shù)據(jù)庫(kù)實(shí)現(xiàn)代碼詳解

    這篇文章主要介紹了Python讀取Hive數(shù)據(jù)庫(kù)實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03

最新評(píng)論