標籤:十分 日文 1.2 頻寬 tool ges 成功 pre 相容
一 瞭解字元編碼的知識儲備
1. 文字編輯器存取檔案的原理(nodepad++,pycharm,word)
開啟編輯器就開啟了啟動了一個進程,是在記憶體中的,所以在編輯器編寫的內容也都是存放與記憶體中的,斷電後資料丟失
因而需要儲存到硬碟上,點擊儲存按鈕,就從記憶體中把資料刷到了硬碟上。
在這一點上,我們編寫一個py檔案(沒有執行),跟編寫其他檔案沒有任何區別,都只是在編寫一堆字元而已。
2. python解譯器執行py檔案的原理 ,例如python test.py
第一階段:python解譯器啟動,此時就相當於啟動了一個文字編輯器
第二階段:python解譯器相當於文字編輯器,去開啟test.py檔案,從硬碟上將test.py的檔案內容讀入到記憶體中
第三階段:python解譯器解釋執行剛剛載入到記憶體中test.py的代碼
總結:
- python解譯器是解釋執行檔案內容的,因而python解譯器具備讀py檔案的功能,這一點與文字編輯器一樣
- 與文字編輯器不一樣的地方在於,python解譯器不僅可以讀檔案內容,還可以執行檔案內容
二 什麼是字元編碼
電腦要想工作必須通電,也就是說‘電’驅使電腦幹活,而‘電’的特性,就是高低電平(高低平即位元1,低電平即位元0),也就是說電腦只認識數字
編程的目的是讓電腦幹活,而編程的結果說白了只是一堆字元,也就是說我們編程最終要實現的是:一堆字元驅動電腦幹活
所以必須經過一個過程:
字元--------(翻譯過程)------->數字
這個過程實際就是一個字元如何對應一個特定數位標準,這個標準稱之為字元編碼
三 字元編碼的發展史
階段一:現代電腦起源於美國,最早誕生也是基於英文考慮的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應該是儘可能地節省頻寬,保證資料轉送的穩定性。
四.字元編碼分類(簡單瞭解)
電腦由美國人發明,最早的字元編碼為ASCII,只規定了英文字母數字和一些特殊字元與數位對應關係。最多隻能用 8 位來表示(一個位元組),即:2**8 = 256,所以,ASCII碼最多隻能表示 256 個符號
當然我們程式設計語言都用英文沒問題,ASCII夠用,但是在處理資料時,不同的國家有不同的語言,日本人會在自己的程式中加入日文,中國人會加入中文。
而要表示中文,單拿一個位元組表表示一個漢子,是不可能表達完的(連小學生都認識兩千多個漢字),解決方案只有一個,就是一個位元組用>8位2進位代表,位元越多,代表的變化就多,這樣,就可以儘可能多的表達出不通的漢字
所以中國人規定了自己的標準gb2312編碼,規定了包含中文在內的字元->數位對應關係。
日本人規定了自己的Shift_JIS編碼
韓國人規定了自己的Euc-kr編碼(另外,韓國人說,電腦是他們發明的,要求世界統一用韓國編碼)
這時候問題出現了,精通18國語言的小周同學謙虛的用8國語言寫了一篇文檔,那麼這篇文檔,按照哪國的標準,都會出現亂碼(因為此刻的各種標準都只是規定了自己國家的文字在內的字元跟數位對應關係,如果單純採用一種國家的編碼格式,那麼其餘國家語言的文字在解析時就會出現亂碼)
所以迫切需要一個世界的標準(能包含全世界的語言)於是unicode應運而生(韓國人表示不服,然後沒有什麼卵用)
ascii用1個位元組(8位二進位)代表一個字元
unicode常用2個位元組(16位二進位)代表一個字元,生僻字需要用4個位元組
例:
字母x,用ascii表示是十進位的120,二進位0111 1000
漢字中已經超出了ASCII編碼的範圍,用Unicode編碼是十進位的20013,二進位的01001110 00101101。
字母x,用unicode表示二進位0000 0000 0111 1000,所以unicode相容ascii,也相容萬國,是世界的標準
這時候亂碼問題消失了,所有的文檔我們都使用但是新問題出現了,如果我們的文檔通篇都是英文,你用unicode會比ascii耗費多一倍的空間,在儲存和傳輸上十分的低效
本著節約的精神,又出現了把Unicode編碼轉化為“可變長編碼”的UTF-8編碼。UTF-8編碼把一個Unicode字元根據不同的數字大小編碼成1-6個位元組,常用的英文字母被編碼成1個位元組,漢字通常是3個位元組,只有很生僻的字元才會被編碼成4-6個位元組。如果你要傳輸的文本包含大量英文字元,用UTF-8編碼就能節省空間的:
| 字元 |
ASCII |
Unicode |
UTF-8 |
| A |
01000001 |
00000000 01000001 |
01000001 |
| 中 |
x |
01001110 00101101 |
11100100 10111000 10101101 |
從上面的表格還可以發現,UTF-8編碼有一個額外的好處,就是ASCII編碼實際上可以被看成是UTF-8編碼的一部分,所以,大量只支援ASCII編碼的曆史遺留軟體可以在UTF-8編碼下繼續工作。
五 字元編碼的使用5.1 文字編輯器一鍋端
5.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了,而存檔案時亂碼,則是一種資料的損壞。
5.1.3 文字編輯器pycharm
以gbk格式儲存
以utf-8格式開啟
分析過程?
總結:
無論是何種編輯器,要防止檔案出現亂碼(請一定注意,存放一段代碼的檔案也僅僅只是一個普通檔案而已,此處指的是檔案沒有執行前,我們開啟檔案時出現的亂碼)
核心法則就是,檔案以什麼編碼儲存的,就以什麼編碼方式開啟
5.2 程式的執行
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編碼的二進位。
5.3 python2與python3的區別5.3.1 在python2中有兩種字串類型str和unicode
str類型
當python解譯器執行到產生字串的代碼時(例如s=‘林‘),會申請新的記憶體位址,然後將‘林‘encode成檔案開頭指定的編碼格式,這已經是encode之後的結果了,所以s只能decode
1 #_*_coding:gbk_*_2 #!/usr/bin/env python3 4 x=‘林‘5 # print x.encode(‘gbk‘) #報錯6 print x.decode(‘gbk‘) #結果:林
所以很重要的一點是:
在python2中,str就是編碼後的結果bytes,str=bytes,所以在python2中,unicode字元編碼的結果是str/bytes
#coding:utf-8s=‘林‘ #在執行時,‘林‘會被以conding:utf-8的形式儲存到新的記憶體空間中print repr(s) #‘\xe6\x9e\x97‘ 三個Bytes,證明確實是utf-8print 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,所以只能encodes.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-8s=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用3Bytesprint repr(s2) #‘\xc1\xd6‘ 編碼一個漢字gbk用2Bytesprint type(s) #<type ‘unicode‘>print type(s1) #<type ‘str‘>print type(s2) #<type ‘str‘>
5.3.2 在python三種也有兩種字串類型str和bytes
str是unicode
#coding:utf-8s=‘林‘ #當程式執行時,無需加u,‘林‘也會被以unicode形式儲存新的記憶體空間中,#s可以直接encode成任意編碼格式s.encode(‘utf-8‘)s.encode(‘gbk‘)print(type(s)) #<class ‘str‘>
bytes是bytes
#coding:utf-8s=‘林‘ #當程式執行時,無需加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‘>
python字元編碼