閱讀目錄
一 了解字符編碼的知識儲備
一 計算機基礎知識
?
二 文本編輯器存取文件的原理(nodepad++,pycharm,word)
#1、打開編輯器就打開了啟動了一個進程,是在內存中的,所以,用編輯器編寫的內容也都是存放與內存中的,斷電后數據丟失#2、要想永久保存,需要點擊保存按鈕:編輯器把內存的數據刷到了硬盤上。#3、在我們編寫一個py文件(沒有執行),跟編寫其他文件沒有任何區別,都只是在編寫一堆字符而已。
三 python解釋器執行py文件的原理 ,例如python test.py
#第一階段:python解釋器啟動,此時就相當于啟動了一個文本編輯器#第二階段:python解釋器相當于文本編輯器,去打開test.py文件,從硬盤上將test.py的文件內容讀入到內存中(小復習:pyhon的解釋性,決定了解釋器只關心文件內容,不關心文件后綴名)#第三階段:python解釋器解釋執行剛剛加載到內存中test.py的代碼( ps:在該階段,即真正執行代碼時,才會識別python的語法,執行文件內代碼,當執行到name="egon"時,會開辟內存空間存放字符串"egon")
四?總結python解釋器與文件本編輯的異同
#1、相同點:python解釋器是解釋執行文件內容的,因而python解釋器具備讀py文件的功能,這一點與文本編輯器一樣#2、不同點:文本編輯器將文件內容讀入內存后,是為了顯示或者編輯,根本不去理會python的語法,而python解釋器將文件內容讀入內存后,可不是為了給你瞅一眼python代碼寫的啥,而是為了執行python代碼、會識別python語法。
二 字符編碼介紹
一 什么是字符編碼
計算機要想工作必須通電,即用‘電’驅使計算機干活,也就是說‘電’的特性決定了計算機的特性。電的特性即高低電平(人類從邏輯上將二進制數1對應高電平,二進制數0對應低電平),關于磁盤的磁特性也是同樣的道理。結論:計算機只認識數字很明顯,我們平時在使用計算機時,用的都是人類能讀懂的字符(用高級語言編程的結果也無非是在文件內寫了一堆字符),如何能讓計算機讀懂人類的字符?必須經過一個過程:#字符--------(翻譯過程)------->數字 #這個過程實際就是一個字符如何對應一個特定數字的標準,這個標準稱之為字符編碼
二?以下兩個場景下涉及到字符編碼的問題:
#1、一個python文件中的內容是由一堆字符組成的,存取均涉及到字符編碼問題(python文件并未執行,前兩個階段均屬于該范疇)#2、python中的數據類型字符串是由一串字符組成的(python文件執行時,即第三個階段)
三 字符編碼的發展史與分類(了解)
計算機由美國人發明,最早的字符編碼為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編碼下繼續工作。
四 總結字符編碼的發展可分為三個階段(重要)

三 字符編碼應用之文件編輯器
3.1 文本編輯器之nodpad++
?

3.2 文本編輯器之pycharm
以utf-8格式打開(選擇reload)

3.3 文本編輯器之python解釋器
文件test.py以gbk格式保存,內容為:x='林'無論是python2 test.py還是python3 test.py都會報錯(因為python2默認ascii,python3默認utf-8)除非在文件開頭指定#coding:gbk
3.4 總結
!!!總結非常重要的兩點!!!
#1、保證不亂嗎的核心法則就是,字符按照什么標準而編碼的,就要按照什么標準解碼,此處的標準指的就是字符編碼#2、在內存中寫的所有字符,一視同仁,都是unicode編碼,比如我們打開編輯器,輸入一個“你”,我們并不能說“你”就是一個漢字,此時它僅僅只是一個符號,該符號可能很多國家都在使用,根據我們使用的輸入法不同這個字的樣式可能也不太一樣。只有在我們往硬盤保存或者基于網絡傳輸時,才能確定”你“到底是一個漢字,還是一個日本字,這就是unicode轉換成其他編碼格式的過程了
? ? ? ? ? ? ? ? ? unicode----->encode-------->utf-8
? ? ? ? ? ? ? ? ? utf-8-------->decode---------->unicode
#補充:瀏覽網頁的時候,服務器會把動態生成的Unicode內容轉換為UTF-8再傳輸到瀏覽器如果服務端encode的編碼格式是utf-8, 客戶端內存中收到的也是utf-8編碼的結果。
?
四 字符編碼應用之python
4.1 執行python程序的三個階段
python test.py ? (我再強調一遍,執行test.py的第一步,一定是先將文件內容讀入到內存中)
test.py文件內容以gbk格式保存的,內容為:
階段一:啟動python解釋器
階段二:python解釋器此時就是一個文本編輯器,負責打開文件test.py,即從硬盤中讀取test.py的內容到內存中
此時,python解釋器會讀取test.py的第一行內容,#coding:utf-8,來決定以什么編碼格式來讀入內存,這一行就是來設定python解釋器這個軟件的編碼使用的編碼格式這個編碼,可以用sys.getdefaultencoding()查看,如果不在python文件指定頭信息#-*-coding:utf-8-*-,那就使用默認的python2中默認使用ascii,python3中默認使用utf-8?
?
改正:在test.py指定文件頭,字符編碼一定要為gbk,
#coding:gbk 你好啊
階段三:讀取已經加載到內存的代碼(unicode編碼格式),然后執行,執行過程中可能會開辟新的內存空間,比如x="egon"
內存的編碼使用unicode,不代表內存中全都是unicode,在程序執行之前,內存中確實都是unicode,比如從文件中讀取了一行x="egon",其中的x,等號,引號,地位都一樣,都是普通字符而已,都是以unicode的格式存放于內存中的但是程序在執行過程中,會申請內存(與程序代碼所存在的內存是倆個空間)用來存放python的數據類型的值,而python的字符串類型又涉及到了字符的概念比如x="egon",會被python解釋器識別為字符串,會申請內存空間來存放字符串類型的值,至于該字符串類型的值被識別成何種編碼存放,這就與python解釋器的有關了,而python2與python3的字符串類型又有所不同。
4.2 python2與python3字符串類型的區別
一 在python2中有兩種字符串類型str和unicode
str類型
當python解釋器執行到產生字符串的代碼時(例如x='上'),會申請新的內存地址,然后將'上'編碼成文件開頭指定的編碼格式
要想看x在內存中的真實格式,可以將其放入列表中再打印,而不要直接打印,因為直接print()會自動轉換編碼,這一點我們稍后再說。
#coding:gbk x='上' y='下' print([x,y]) #['\xc9\xcf', '\xcf\xc2'] #\x代表16進制,此處是c9cf總共4位16進制數,一個16進制四4個比特位,4個16進制數則是16個比特位,即2個Bytes,這就證明了按照gbk編碼中文用2Bytes
print(type(x),type(y)) #(<type 'str'>, <type 'str'>)
理解字符編碼的關鍵!!!
內存中的數據通常用16進制表示,2位16進制數據代表一個字節,如\xc9,代表兩位16進制,一個字節
gbk存中文需要2個bytes,而存英文則需要1個bytes,它是如何做到的???!!!
gbk會在每個bytes,即8位bit的第一個位作為標志位,標志位為1則表示是中文字符,如果標志位為0則表示為英文字符
x=‘你a好’ 轉成gbk格式二進制位 8bit+8bit+8bit+8bit+8bit=(1+7bit)+(1+7bit)+(0+7bit)+(1+7bit)+(1+7bit)
這樣計算機按照從左往右的順序讀:
#連續讀到前兩個括號內的首位標志位均為1,則構成一個中午字符:你#讀到第三個括號的首位標志為0,則該8bit代表一個英文字符:a#連續讀到后兩個括號內的首位標志位均為1,則構成一個中午字符:好
也就是說,每個Bytes留給我們用來存真正值的有效位數只有7位,而在unicode表中存放的只是這有效的7位,至于首位的標志位與具體的編碼有關,即在unicode中表示gbk的方式為:
(7bit)+(7bit)+(7bit)+(7bit)+(7bit)
?
按照上圖翻譯的結果,我們可以去unicode關于漢字的對應關系中去查:鏈接:https://pan.baidu.com/s/1dEV3RYp
?
可以看到“”上“”對應的gbk(G0代表的是gbk)編碼就為494F,即我們得出的結果,而上對應的unicode編碼為4E0A,我們可以將gbk-->decode-->unicode
#coding:gbk x='上'.decode('gbk') y='下'.decode('gbk') print([x,y]) #[u'\u4e0a', u'\u4e0b']
unicode類型
當python解釋器執行到產生字符串的代碼時(例如s=u'林'),會申請新的內存地址,然后將'林'以unicode的格式存放到新的內存空間中,所以s只能encode,不能decode
#coding:gbk x=u'上' #等同于 x='上'.decode('gbk') y=u'下' #等同于 y='下'.decode('gbk') print([x,y]) #[u'\u4e0a', u'\u4e0b']
print(type(x),type(y)) #(<type 'unicode'>, <type 'unicode'>)
打印到終端
對于print需要特別說明的是:
當程序執行時,比如
x='上' #gbk下,字符串存放為\xc9\xcf
print(x) #這一步是將x指向的那塊新的內存空間(非代碼所在的內存空間)中的內存,打印到終端,按理說應該是存的什么就打印什么,但打印\xc9\xcf,對一些不熟知python編碼的程序員,立馬就懵逼了,所以龜叔自作主張,在print(x)時,使用終端的編碼格式,將內存中的\xc9\xcf轉成字符顯示,此時就需要終端編碼必須為gbk,否則無法正常顯示原內容:上
對于unicode格式的數據來說,無論怎么打印,都不會亂碼
unicode這么好,不會亂碼,那python2為何還那么別扭,搞一個str出來呢?python誕生之時,unicode并未像今天這樣普及,很明顯,好的東西你能看得見,龜叔早就看見了,龜叔在python3中將str直接存成unicode,我們定義一個str,無需加u前綴,就是一個unicode,屌不屌?
?
二 在python3 中也有兩種字符串類型str和bytes
str是unicode
#coding:gbk x='上' #當程序執行時,無需加u,'上'也會被以unicode形式保存新的內存空間中,print(type(x)) #<class 'str'>#x可以直接encode成任意編碼格式 print(x.encode('gbk')) #b'\xc9\xcf' print(type(x.encode('gbk'))) #<class 'bytes'>
很重要的一點是:看到python3中x.encode('gbk') 的結果\xc9\xcf正是python2中的str類型的值,而在python3是bytes類型,在python2中則是str類型
于是我有一個大膽的推測:python2中的str類型就是python3的bytes類型,于是我查看python2的str()源碼,發現