1. 程式人生 > >python字符編碼

python字符編碼

height 計算 速度慢 char bytes bit lose line 後來

1. 字符編碼簡介

階段一:現代計算機起源於美國,最早誕生也是基於英文考慮的ASCII

ASCII:一個Bytes代表一個字符(英文字符/鍵盤上的所有其他字符),1Bytes=8bit,8bit可以表示0-2**8-1種變化,即可以表示256個字符

ASCII最初只用了後七位,127個數字,已經完全能夠代表鍵盤上所有的字符了(英文字符/鍵盤的所有其他字符)

後來為了將拉丁文也編碼進了ASCII表,將最高位也占用了

階段二:為了滿足中文,中國人定制了GBK

GBK:2Bytes代表一個字符

為了滿足其他國家,各個國家紛紛定制了自己的編碼

日本把日文編到Shift_JIS裏,韓國把韓文編到Euc-kr裏

階段三:各國有各國的標準,就會不可避免地出現沖突,結果就是,在多語言混合的文本中,顯示出來會有亂碼。

於是產生了unicode, 統一用2Bytes代表一個字符, 2**16-1=65535,可代表6萬多個字符,因而兼容萬國語言

但對於通篇都是英文的文本來說,這種編碼方式無疑是多了一倍的存儲空間(二進制最終都是以電或者磁的方式存儲到存儲介質中的)

於是產生了UTF-8,對英文字符只用1Bytes表示,對中文字符用3Bytes

需要強調的一點是:

unicode:簡單粗暴,所有字符都是2Bytes,優點是字符->數字的轉換速度快,缺點是占用空間大

utf-8:精準,對不同的字符用不同的長度表示,優點是節省空間,缺點是:字符->數字的轉換速度慢,因為每次都需要計算出字符需要多長的Bytes才能夠準確表示

內存中使用的編碼是unicode,用空間換時間(程序都需要加載到內存才能運行,因而內存應該是盡可能的保證快) 硬盤中或者網絡傳輸用utf-8,網絡I/O延遲或磁盤I/O延遲要遠大與utf-8的轉換延遲,而且I/O應該是盡可能地節省帶寬,保證數據傳輸的穩定性。

2.字符編碼的使用

2.1 文本編輯器一鍋端

技術分享


2.1.2 文本編輯器nodpad++

技術分享

技術分享

技術分享

技術分享

分析過程?什麽是亂碼

文件從內存刷到硬盤的操作簡稱存文件

文件從硬盤讀到內存的操作簡稱讀文件

亂碼一:存文件時就已經亂碼

存文件時,由於文件內有各個國家的文字,我們單以shiftjis去存,

本質上其他國家的文字由於在shiftjis中沒有找到對應關系而導致存儲失敗,用open函數的write可以測試,f=open(‘a.txt‘,‘w‘,encodig=‘shift_jis‘)

f.write(‘你瞅啥\n何を見て\n‘) #‘你瞅啥‘因為在shiftjis中沒有找到對應關系而無法保存成功,只存‘何を見て\n‘可以成功

但當我們用文件編輯器去存的時候,編輯器會幫我們做轉換,保證中文也能用shiftjis存儲(硬存,必然亂碼),這就導致了,存文件階段就已經發生亂碼

此時當我們用shiftjis打開文件時,日文可以正常顯示,而中文則亂碼了

再或者,存文件時:

f=open(‘a.txt‘,‘wb‘)

f.write(‘何を見て\n‘.encode(‘shift_jis‘))
f.write(‘你愁啥\n‘.encode(‘gbk‘))
f.write(‘你愁啥\n‘.encode(‘utf-8‘))
f.close()

以任何編碼打開文件a.txt都會出現其余兩個無法正常顯示的問題

亂碼二:存文件時不亂碼而讀文件時亂碼

存文件時用utf-8編碼,保證兼容萬國,不會亂碼,而讀文件時選擇了錯誤的解碼方式,比如gbk,則在讀階段發生亂碼,讀階段發生亂碼是可以解決的,選對正確的解碼方式就ok了,而存文件時亂碼,則是一種數據的損壞。


2.1.3 文本編輯器pycharm

以gbk格式保存

技術分享

以utf-8格式打開

技術分享

分析過程?

總結:

無論是何種編輯器,要防止文件出現亂碼(請一定註意,存放一段代碼的文件也僅僅只是一個普通文件而已,此處指的是文件沒有執行前,我們打開文件時出現的亂碼)

核心法則就是,文件以什麽編碼保存的,就以什麽編碼方式打開


3. 程序的執行

python test.py (我再強調一遍,執行test.py的第一步,一定是先將文件內容讀入到內存中)

階段一:啟動python解釋器

階段二:python解釋器此時就是一個文本編輯器,負責打開文件test.py,即從硬盤中讀取test.py的內容到內存中

此時,python解釋器會讀取test.py的第一行內容,#coding:utf-8,來決定以什麽編碼格式來讀入內存,這一行就是來設定python解釋器這個軟件的編碼使用的編碼格式這個編碼,

可以用sys.getdefaultencoding()查看,如果不在python文件指定頭信息#-*-coding:utf-8-*-,那就使用默認的

python2中默認使用ascii,python3中默認使用utf-8

技術分享

技術分享

階段三:讀取已經加載到內存的代碼(unicode編碼的二進制),然後執行,執行過程中可能會開辟新的內存空間,比如x="egon"

內存的編碼使用unicode,不代表內存中全都是unicode編碼的二進制,

在程序執行之前,內存中確實都是unicode編碼的二進制,比如從文件中讀取了一行x="egon",其中的x,等號,引號,地位都一樣,都是普通字符而已,都是以unicode編碼的二進制形式存放與內存中的

但是程序在執行過程中,會申請內存(與程序代碼所存在的內存是倆個空間),可以存放任意編碼格式的數據,比如x="egon",會被python解釋器識別為字符串,會申請內存空間來存放"hello",然後讓x指向該內存地址,此時新申請的該內存地址保存也是unicode編碼的egon,如果代碼換成x="egon".encode(‘utf-8‘),那麽新申請的內存空間裏存放的就是utf-8編碼的字符串egon了

針對python3如下圖

技術分享

瀏覽網頁的時候,服務器會把動態生成的Unicode內容轉換為UTF-8再傳輸到瀏覽器

技術分享

如果服務端encode的編碼格式是utf-8, 客戶端內存中收到的也是utf-8編碼的二進制。


4. python2與python3的區別


4.1 在python2中有兩種字符串類型str和unicode

str類型

當python解釋器執行到產生字符串的代碼時(例如s=‘林‘),會申請新的內存地址,然後將‘林‘encode成文件開頭指定的編碼格式,這已經是encode之後的結果了,所以s只能decode

1 #_*_coding:gbk_*_
2 #!/usr/bin/env python
3 
4 x=‘林‘
5 # print x.encode(‘gbk‘) #報錯
6 print x.decode(‘gbk‘) #結果:林

所以很重要的一點是:

在python2中,str就是編碼後的結果bytes,str=bytes,所以在python2中,unicode字符編碼的結果是str/bytes

技術分享

#coding:utf-8
s=‘林‘ #在執行時,‘林‘會被以conding:utf-8的形式保存到新的內存空間中

print repr(s) #‘\xe6\x9e\x97‘ 三個Bytes,證明確實是utf-8
print type(s) #<type ‘str‘>

s.decode(‘utf-8‘)
# s.encode(‘utf-8‘) #報錯,s為編碼後的結果bytes,所以只能decode

unicode類型

當python解釋器執行到產生字符串的代碼時(例如s=u‘林‘),會申請新的內存地址,然後將‘林‘以unicode的格式存放到新的內存空間中,所以s只能encode,不能decode

s=u‘林‘
print repr(s) #u‘\u6797‘
print type(s) #<type ‘unicode‘>


# s.decode(‘utf-8‘) #報錯,s為unicode,所以只能encode
s.encode(‘utf-8‘) 

打印到終端

對於print需要特別說明的是:

當程序執行時,比如

x=‘林‘

print(x) #這一步是將x指向的那塊新的內存空間(非代碼所在的內存空間)中的內存,打印到終端,而終端仍然是運行於內存中的,所以這打印可以理解為從內存打印到內存,即內存->內存,unicode->unicode

對於unicode格式的數據來說,無論怎麽打印,都不會亂碼

python3中的字符串與python2中的u‘字符串‘,都是unicode,所以無論如何打印都不會亂碼

在pycharm中

技術分享

在windows終端

技術分享

但是在python2中存在另外一種非unicode的字符串,此時,print x,會按照終端的編碼執行x.decode(‘終端編碼‘),變成unicode後,再打印,此時終端編碼若與文件開頭指定的編碼不一致,亂碼就產生了

在pycharm中(終端編碼為utf-8,文件編碼為utf-8,不會亂碼)

技術分享

在windows終端(終端編碼為gbk,文件編碼為utf-8,亂碼產生)

技術分享

思考題:

分別驗證在pycharm中和cmd中下述的打印結果

#coding:utf-8
s=u‘林‘ #當程序執行時,‘林‘會被以unicode形式保存新的內存空間中


#s指向的是unicode,因而可以編碼成任意格式,都不會報encode錯誤
s1=s.encode(‘utf-8‘)
s2=s.encode(‘gbk‘)
print s1 #打印正常否?
print s2 #打印正常否


print repr(s) #u‘\u6797‘
print repr(s1) #‘\xe6\x9e\x97‘ 編碼一個漢字utf-8用3Bytes
print repr(s2) #‘\xc1\xd6‘ 編碼一個漢字gbk用2Bytes

print type(s) #<type ‘unicode‘>
print type(s1) #<type ‘str‘>
print type(s2) #<type ‘str‘>


4.2 在python三種也有兩種字符串類型str和bytes

str是unicode

#coding:utf-8
s=‘林‘ #當程序執行時,無需加u,‘林‘也會被以unicode形式保存新的內存空間中,

#s可以直接encode成任意編碼格式
s.encode(‘utf-8‘)
s.encode(‘gbk‘)

print(type(s)) #<class ‘str‘>

bytes是bytes

#coding:utf-8
s=‘林‘ #當程序執行時,無需加u,‘林‘也會被以unicode形式保存新的內存空間中,

#s可以直接encode成任意編碼格式
s1=s.encode(‘utf-8‘)
s2=s.encode(‘gbk‘)



print(s) #林
print(s1) #b‘\xe6\x9e\x97‘ 在python3中,是什麽就打印什麽
print(s2) #b‘\xc1\xd6‘ 同上

print(type(s)) #<class ‘str‘>
print(type(s1)) #<class ‘bytes‘>
print(type(s2)) #<class ‘bytes‘>

參考http://www.cnblogs.com/linhaifeng/articles/5950339.html

python字符編碼