一、計算機(jī)基礎(chǔ)知識
二、文本編輯器存取文件的原理(nodepad++,pycharm,word)
#1、打開編輯器就打開了啟動了一個進(jìn)程,是在內(nèi)存中的,所以,用編輯器編寫的內(nèi)容也都是存放與內(nèi)存中的,斷電后數(shù)據(jù)丟失
#2、要想永久保存,需要點(diǎn)擊保存按鈕:編輯器把內(nèi)存的數(shù)據(jù)刷到了硬盤上。
#3、在我們編寫一個py文件(沒有執(zhí)行),跟編寫其他文件沒有任何區(qū)別,都只是在編寫一堆字符而已。
三、python解釋器執(zhí)行py文件的原理 ,例如python test.py
總共分三個執(zhí)行階段:
第一階段:Python解釋器啟動,此時就相當(dāng)于啟動了一個文本編輯器
第二階段:Python解釋器相當(dāng)于文本編輯器,去打開test.py文件,從硬盤上將test.py 的文件內(nèi)容讀到內(nèi)存中(小復(fù)習(xí):Python的解釋性,決定了解釋器只關(guān)心文件內(nèi)容,不關(guān)心文件名后綴名)
第三階段:Python解釋器執(zhí)行剛剛加載到內(nèi)存中的test.py的代碼(ps:在該階段,即真正執(zhí)行代碼時,才會識別Python的語法,執(zhí)行文件內(nèi)代碼,當(dāng)執(zhí)行到name='parker'時,會開辟內(nèi)存空間存放字符串‘parker’)
四 總結(jié)python解釋器與文件本編輯的異同
#1、相同點(diǎn):Python解釋器是執(zhí)行文件內(nèi)容的,因而python解釋器具備讀py文件的功能,這一點(diǎn)與文本編輯器一樣
#2、不同點(diǎn):文本編輯器將文件內(nèi)容讀到內(nèi)存后,是為了顯示或者編輯,根本不會去理Python的語法,而Python解釋器將文件內(nèi)容讀入內(nèi)存后,可不是為了給你瞅一眼Python代碼寫的是啥,而是為了執(zhí)行Python代碼、會識別Python語法。
字符編碼介紹
一、什么是字符編碼
計算機(jī)要想工作必須通電,即用‘電’驅(qū)使計算機(jī)干活,也就是說‘電’的特性決定了計算機(jī)的特性。電的特性即高低電平(人類從邏輯上將二進(jìn)制數(shù)1對應(yīng)高電平,二進(jìn)制數(shù)0對應(yīng)低電平),關(guān)于磁盤的磁特性也是同樣的道理。結(jié)論:計算機(jī)只認(rèn)識數(shù)字
很明顯,我們平時在使用計算機(jī)時,用的都是人類能讀懂的字符(用高級語言編程的結(jié)果也無非是在文件內(nèi)寫了一堆字符),如何能讓計算機(jī)讀懂人類的字符?
必須經(jīng)過一個過程:
#字符--------(翻譯過程)------->數(shù)字
#這個過程實(shí)際就是一個字符如何對應(yīng)一個特定數(shù)字的標(biāo)準(zhǔn),這個標(biāo)準(zhǔn)稱之為字符編碼
二、以下兩個場景涉及到字符編碼問題:
#1、一個Python文件中的內(nèi)容是有一對字符組成的,存取涉及到字符編碼問題(Python文件沒執(zhí)行,前兩個階段均屬于該范疇)
#2、Python中的數(shù)據(jù)類型字符串是由一串字符組成的(Python文件執(zhí)行時,即第三個階段)
總結(jié)字符編碼的發(fā)展可分為三個階段(重要)
#階段一:現(xiàn)代計算機(jī)起源于美國,最早誕生也是基于英文考慮的ASCII
ASCII:一個Bytes代表一個字符(英文字符/鍵盤上的所有其他字符),1Bytes=8bit,8bit可以表示0-2**8-1種變化,即可以表示256個字符
ASCII最初只用了后七位,127個數(shù)字,已經(jīng)完全能夠代表鍵盤上所有的字符了(英文字符/鍵盤的所有其他字符),后來為了將拉丁文也編碼進(jìn)了ASCII表,將最高位也占用了
#階段二:為了滿足中文和英文,中國人定制了GBK
GBK:2Bytes代表一個中文字符,1Bytes表示一個英文字符
為了滿足其他國家,各個國家紛紛定制了自己的編碼
日本把日文編到Shift_JIS里,韓國把韓文編到Euc-kr里
#階段三:各國有各國的標(biāo)準(zhǔn),就會不可避免地出現(xiàn)沖突,結(jié)果就是,在多語言混合的文本中,顯示出來會有亂碼。如何解決這個問題呢???
#!!!!!!!!!!!!非常重要!!!!!!!!!!!!
說白了亂碼問題的本質(zhì)就是不統(tǒng)一,如果我們能統(tǒng)一全世界,規(guī)定全世界只能使用一種文字符號,然后統(tǒng)一使用一種編碼,那么亂碼問題將不復(fù)存在,
ps:就像當(dāng)年秦始皇統(tǒng)一中國一樣,書同文車同軌,所有的麻煩事全部解決
很明顯,上述的假設(shè)是不可能成立的。很多地方或老的系統(tǒng)、應(yīng)用軟件仍會采用各種各樣的編碼,這是歷史遺留問題。于是我們必須找出一種解決方案或者說編碼方案,需要同時滿足:
#1、能夠兼容萬國字符
#2、與全世界所有的字符編碼都有映射關(guān)系,這樣就可以轉(zhuǎn)換成任意國家的字符編碼
這就是unicode(定長), 統(tǒng)一用2Bytes代表一個字符, 雖然2**16-1=65535,但unicode卻可以存放100w+個字符,因為unicode存放了與其他編碼的映射關(guān)系,準(zhǔn)確地說unicode并不是一種嚴(yán)格意義上的字符編碼表,下載pdf來查看unicode的詳情:
鏈接:https://pan.baidu.com/s/1dEV3RYp
很明顯對于通篇都是英文的文本來說,unicode的式無疑是多了一倍的存儲空間(二進(jìn)制最終都是以電或者磁的方式存儲到存儲介質(zhì)中的)
于是產(chǎn)生了UTF-8(可變長,全稱Unicode Transformation Format),對英文字符只用1Bytes表示,對中文字符用3Bytes,對其他生僻字用更多的Bytes去存
#總結(jié):內(nèi)存中統(tǒng)一采用unicode,浪費(fèi)空間來換取可以轉(zhuǎn)換成任意編碼(不亂碼),硬盤可以采用各種編碼,如utf-8,保證存放于硬盤或者基于網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)量很小,提高傳輸效率與穩(wěn)定性。
三、字符編碼應(yīng)用之文件編輯器
3.1 文本編輯器值notepad++
亂碼分析
首先明確概念
1、文件從內(nèi)存刷到硬盤的操作簡稱存文件
2、文件從硬盤讀到內(nèi)存的操作簡稱讀文件
亂碼的兩種情況:
亂碼一:存文件時就已經(jīng)亂碼
存文件時,由于文件內(nèi)有各個國家的文字,我們單以shiftjis去存,
本質(zhì)上其他國家的文字由于在shiftjis中沒有找到對應(yīng)關(guān)系而導(dǎo)致存儲失敗
但當(dāng)我們硬要存的時候,編輯并不會報錯(難道你的編碼錯誤,編輯器這個軟件就跟著崩潰了嗎???),但毫無疑問,不能存而硬存,肯定是亂存了,即存文件階段就已經(jīng)發(fā)生亂碼
而當(dāng)我們用shiftjis打開文件時,日文可以正常顯示,而中文則亂碼了
用open模擬編輯器的過程
可以用open函數(shù)的write可以測試,f=open('a.txt','w',encodig='shift_jis'
f.write('你瞅啥\n何を見て\n') #'你瞅啥'因為在shiftjis中沒有找到對應(yīng)關(guān)系而無法保存成功,只存'何を見て\n'可以成功
以任何編碼打開文件a.txt都會出現(xiàn)其余兩個無法正常顯示的問題
f=open('a.txt','wb')
f.write('何を見て\n'.encode('shift_jis'))
f.write('你愁啥\n'.encode('gbk'))
f.write('你愁啥\n'.encode('utf-8'))
f.close()
亂碼二:存文件時不亂碼而讀文件時亂碼
存文件時用utf-8編碼,保證兼容萬國,不會亂碼,而讀文件時選擇了錯誤的解碼方式,比如gbk,則在讀階段發(fā)生亂碼,讀階段發(fā)生亂碼是可以解決的,選對正確的解碼方式就ok了。
3.2 文本編輯器之pycharm
以utf-8格式打開(選擇reload)
pycharm中convert 和reload 的區(qū)別
#reload與convert的區(qū)別:
pycharm非常強(qiáng)大,提供了自動幫我們convert轉(zhuǎn)換的功能,即將字符按照正確的格式轉(zhuǎn)換
要自己探究字符編碼的本質(zhì),還是不要用這個
我們選擇reload,即按照某種編碼重新加載文件
pycharm中:reload與convert的區(qū)別
3.3 文本編輯器之python解釋器
文件test.py以gbk格式保存,內(nèi)容為:
x='林'
無論是
python2 test.py
還是
python3 test.py
都會報錯(因為python2默認(rèn)ascii,python3默認(rèn)utf-8)
除非在文件開頭指定#coding:gbk
總結(jié)
!!!非常重要的兩點(diǎn)!!!
1、保證不亂嗎的核心法則就是,字符按照什么標(biāo)準(zhǔn)而編碼的,就要按照什么標(biāo)準(zhǔn)解碼,此處的標(biāo)準(zhǔn)指的就是字符編碼
2、在內(nèi)存中寫的所有字符,一視同仁,都是unicode編碼,比如我們打開編輯器,輸入一個“你”,我們并不能說“你”就是一個漢字,此時它僅僅只是一個符號,該符號可能很多國家都在使用,根據(jù)我們使用的輸入法不同這個字的樣式可能也不太一樣。只有在我們往硬盤保存或者基于網(wǎng)絡(luò)傳輸時,才能確定”你“到底是一個漢字,還是一個日本字,這就是unicode轉(zhuǎn)換成其他編碼格式的過程了
unicode----->encode-------->utf-8
utf-8-------->decode---------->unicode
#補(bǔ)充
瀏覽網(wǎng)頁的時候,服務(wù)器會把動態(tài)生成的Unicode內(nèi)容轉(zhuǎn)換為UTF-8再傳輸?shù)綖g覽器
如果服務(wù)端encode的編碼格式是utf-8, 客戶端內(nèi)存中收到的也是utf-8編碼的結(jié)果。
四 字符編碼應(yīng)用之python
python test.py(再強(qiáng)調(diào)一遍,執(zhí)行test.py的第一步,一定是先將文件內(nèi)容讀入到內(nèi)存中)
test.py文件內(nèi)容以GBK格式保存的,內(nèi)容為:
階段一:啟動Python解釋器
階段二:Python解釋器此時就是一個文本編輯器,負(fù)責(zé)打開文件test.py,即從硬盤中讀取test.py的內(nèi)容到內(nèi)存中。
取test.py的內(nèi)容到內(nèi)存中
此時,python解釋器會讀取test.py的第一行內(nèi)容,#coding:utf-8,來決定以什么編碼格式來讀入內(nèi)存,這一行就是來設(shè)定python解釋器這個軟件的編碼使用的編碼格式這個編碼,
可以用sys.getdefaultencoding()查看,如果不在python文件指定頭信息#-*-coding:utf-8-*-,那就使用默認(rèn)的
python2中默認(rèn)使用ascii,python3中默認(rèn)使用utf-8
改正:在test.py指定文件頭,字符編碼一定要為gbk,
#coding: gbk
階段三:讀取已經(jīng)加載到內(nèi)存的代碼(Unicode編碼格式),然后執(zhí)行,執(zhí)行過程中可能會開辟新的內(nèi)存空間,比如x = 'parker'
內(nèi)存的編碼使用unicode,不代表內(nèi)存中全都是unicode,
在程序執(zhí)行之前,內(nèi)存中確實(shí)都是unicode,比如從文件中讀取了一行x="egon",其中的x,等號,引號,地位都一樣,都是普通字符而已,都是以unicode的格式存放于內(nèi)存中的
但是程序在執(zhí)行過程中,會申請內(nèi)存(與程序代碼所存在的內(nèi)存是倆個空間)用來存放python的數(shù)據(jù)類型的值,而python的字符串類型又涉及到了字符的概念
比如x="egon",會被python解釋器識別為字符串,會申請內(nèi)存空間來存放字符串類型的值,至于該字符串類型的值被識別成何種編碼存放,這就與python解釋器的有關(guān)了,而python2與python3的字符串類型又有所不同。
python2 與Python3字符串類型區(qū)別
一、在Python2中有兩種字符串類型str和Unicode
Str類型
當(dāng)Python解釋器執(zhí)行到產(chǎn)生字符串的代碼時(例如x = 'code'),會申請新的內(nèi)存地址,然后將‘code’編碼成文件開頭指定的編碼格式
要想x在內(nèi)存中的真實(shí)格式,可以將其放入列表中打印,而不要直接打印,因為print()會自動轉(zhuǎn)換編碼,這一點(diǎn)稍后再說。
#coding:gbk
x='上'
y='下'
print([x,y]) #['\xc9\xcf', '\xcf\xc2']
#\x代表16進(jìn)制,此處是c9cf總共4位16進(jìn)制數(shù),一個16進(jìn)制四4個比特位,4個16進(jìn)制數(shù)則是16個比特位,即2個Bytes,這就證明了按照gbk編碼中文用2Bytes
print(type(x),type(y)) #(<type 'str'>, <type 'str'>)
理解字符編碼的關(guān)鍵!!!
內(nèi)存中的數(shù)據(jù)常用16進(jìn)制表示,2位16進(jìn)制數(shù)據(jù)代表一個字節(jié),如\xc9,代表兩位16進(jìn)制,一個字節(jié)
gbk存中文需要2個bytes,而英文則需要1個bytes,它是如何做到的?
gbk會在每個bytes,即8位bit的第一個位作為標(biāo)志位,標(biāo)志位為1則表示是中文字符,如果標(biāo)志位為0則表示為英文字符
x=‘你a好’
轉(zhuǎn)成gbk格式二進(jìn)制位
8bit+8bit+8bit+8bit+8bit=(1+7bit)+(1+7bit)+(0+7bit)+(1+7bit)+(1+7bit)
這樣計算機(jī)按照從左往右的順序讀:
#連續(xù)讀到前兩個括號內(nèi)的首位標(biāo)志位均為1,則構(gòu)成一個中午字符:你
#讀到第三個括號的首位標(biāo)志為0,則該8bit代表一個英文字符:a
#連續(xù)讀到后兩個括號內(nèi)的首位標(biāo)志位均為1,則構(gòu)成一個中午字符:好
也就是說,每個Bytes留給我們用來存真正值的有效位數(shù)只有7位,而在unicode表中存放的只是這有效的7位,至于首位的標(biāo)志位與具體的編碼有關(guān),即在unicode中表示gbk的方式為:
(7bit)+(7bit)+(7bit)+(7bit)+(7bit)
unicode類型
當(dāng)python解釋器執(zhí)行到產(chǎn)生字符串的代碼時(例如s=u'林'),會申請新的內(nèi)存地址,然后將'林'以unicode的格式存放到新的內(nèi)存空間中,所以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需要特別說明的是:
當(dāng)程序執(zhí)行時,比如
x='上' #gbk下,字符串存放為\xc9\xcf
print(x) #這一步是將x指向的那塊新的內(nèi)存空間(非代碼所在的內(nèi)存空間)中的內(nèi)存,打印到終端,按理說應(yīng)該是存的什么就打印什么,但打印\xc9\xcf,對一些不熟知python編碼的程序員,立馬就懵逼了,所以龜叔自作主張,在print(x)時,使用終端的編碼格式,將內(nèi)存中的\xc9\xcf轉(zhuǎn)成字符顯示,此時就需要終端編碼必須為gbk,否則無法正常顯示原內(nèi)容:上
對于Unicode格式的數(shù)據(jù)來說,無論怎么打印,都不會亂碼
很明顯,在Python2 中,Unicode不會亂碼
二、在Python3中也有兩種字符串類型 str和bytes
str是Unicode
#coding:gbk
x='上' #當(dāng)程序執(zhí)行時,無需加u,'上'也會被以unicode形式保存新的內(nèi)存空間中,
print(type(x)) #<class 'str'>
#x可以直接encode成任意編碼格式
print(x.encode('gbk')) #b'\xc9\xcf'
print(type(x.encode('gbk'))) #<class 'bytes'>
很重要的一點(diǎn)是:看到python3中x.encode('gbk') 的結(jié)果\xc9\xcf正是python2中的str類型的值,而在python3是bytes類型,在python2中則是str類型
于是我有一個大膽的推測:python2中的str類型就是python3的bytes類型,于是我查看python2的str()源碼,發(fā)現(xiàn)