06-01 字符編碼


一 引入

​ 字符串類型、文本文件的內容都是由字符組成的,但凡涉及到字符的存取,都需要考慮字符編碼的問題。

​ 字符編碼這個知識點的典型特征就是理論多、結論少,但對於開發而言只需要記住結論即可,下面讓我們來一點點介紹它

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222036024-1551235292.jpg)

二 知識儲備

2.1 三大核心硬件

所有軟件都是運行硬件之上的,與運行軟件相關的三大核心硬件為cpu、內存、硬盤,我們需要明確三點

#1、軟件運行前,軟件的代碼及其相關數據都是存放於硬盤中的

#2、任何軟件的啟動都是將數據從硬盤中讀入內存,然后cpu從內存中取出指令並執行

#3、軟件運行過程中產生的數據最先都是存放於內存中的,若想永久保存軟件產生的數據,則需要將數據由內存寫入硬盤
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222055052-418545065.jpg)

2.2 文本編輯器讀取文件內容的流程

#階段1、啟動一個文件編輯器(文本編輯器如nodepad++,pycharm,word)

#階段2、文件編輯器會將文件內容從硬盤讀入內存

#階段3、文本編輯器會將剛剛讀入內存中的內容顯示到屏幕上

2.3 python解釋器執行文件的流程

以python test.py為例,執行流程如下

#階段1、啟動python解釋器,此時就相當於啟動了一個文本編輯器

#階段2、python解釋器相當於文本編輯器,從硬盤上將test.py的內容讀入到內存中

#階段3、python解釋器解釋執行剛剛讀入的內存的內容,開始識別python語法 

2.4 總結

python解釋器與文件本編輯的異同如下

#1、相同點:前兩個階段二者完全一致,都是將硬盤中文件的內容讀入內存,詳解如下
python解釋器是解釋執行文件內容的,因而python解釋器具備讀py文件的功能,這一點與文本編輯器一樣

#2、不同點:在階段3時,針對內存中讀入的內容處理方式不同,詳解如下
文本編輯器將文件內容讀入內存后,是為了顯示或者編輯,根本不去理會python的語法,而python解釋器將文件內容讀入內存后,可不是為了給你瞅一眼python代碼寫的啥,而是為了執行python代碼、會識別python語法)
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222119626-348397.jpg)

三、字符編碼介紹

3.1 什么是字符編碼?

人類在與計算機交互時,用的都是人類能讀懂的字符,如中文字符、英文字符、日文字符等

而計算機只能識別二進制數,詳解如下

#二進制數即由0和1組成的數字,例如010010101010。計算機是基於電工作的,電的特性即高低電平,人類從邏輯層面將高電平對應為數字1,低電平對應為數字0,這直接決定了計算機可以識別的是由0和1組成的數字

毫無疑問,由人類的字符到計算機中的數字,必須經歷一個過程,如下

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222149047-2131754315.jpg)

翻譯的過程必須參照一個特定的標准,該標准稱之為字符編碼表,該表上存放的就是字符與數字一一對應的關系。

字符編碼中的編碼指的是翻譯或者轉換的意思,即將人能理解的字符翻譯成計算機能識別的數字

3.2 字符編碼表的發展史 (了解)

字符編碼的發展經歷了三個重要的階段,如下

3.2.1 階段一:一家獨大

現代計算機起源於美國,所以最先考慮僅僅是讓計算機識別英文字符,於是誕生了ASCII表

# ASCII表的特點:
	1、只有英文字符與數字的一一對應關系
	2、一個英文字符對應1Bytes,1Bytes=8bit,8bit最多包含256個數字,可以對應256個字符,足夠表示所有英文字符
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222204993-1496149117.jpg)

3.2.2 階段二:諸侯割據、天下大亂

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222216784-1680223704.jpg)

為了讓計算機能夠識別中文和英文,中國人定制了GBK

# GBK表的特點:
	1、只有中文字符、英文字符與數字的一一對應關系
	2、一個英文字符對應1Bytes
	   一個中文字符對應2Bytes	
	   補充說明:
	   1Bytes=8bit,8bit最多包含256個數字,可以對應256個字符,足夠表示所有英文字符
	   2Bytes=16bit,16bit最多包含65536個數字,可以對應65536個字符,足夠表示所有中文字符

每個國家都各自的字符,為讓計算機能夠識別自己國家的字符外加英文字符,各個國家都制定了自己的字符編碼表

# Shift_JIS表的特點:
	1、只有日文字符、英文字符與數字的一一對應關系

# Euc-kr表的特點:
	1、只有韓文字符、英文字符與數字的一一對應關系


此時,美國人用的計算機里使用字符編碼標准是ASCII、中國人用的計算機里使用字符編碼標准是GBK、日本人用的計算機里使用字符編碼標准是Shift_JIS,如下圖所示,

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222231337-988216061.jpg)

字符編碼發展到了這個階段,可以用一句話概括:諸侯割據、天下大亂,詳解如下

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222246451-2039502025.jpg)

圖1中,文本編輯存取文件的原理如下

文本文件內容全都為字符,無論存取都是涉及到字符編碼問題
#1、存文本文件
人類通過文本編輯器輸入的字符會被轉化成ASCII格式的二進制存放於內存中,如果需要永久保存,則直接將內存中的ASCII格式的二進制寫入硬盤

#2、讀文本文件
直接將硬盤中的ASCII格式的二進制讀入內存,然后通過ASCII表反解成英文字符

圖2圖3都是相同的過程,此時無論是存還是取由於采用的字符編碼表一樣,所以肯定不會出現亂碼問題,但問題是在美國人用的計算機里只能輸入英文字符,而在中國人用的計算機里只能輸入中文字符和英文字符....,毫無疑問我們希望計算機允許我們輸入萬國字符均可識別、不亂碼,而現階段計算機采用的字符編碼ASCII、GBK、Shift_JIS都無法識別萬國字符,所以我們必須定制一個兼容萬國字符的編碼表,請看階段三

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222324934-319721262.jpg)

3.2.3 階段三:分久必合

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222420807-206904855.jpg)

unicode於1990年開始研發,1994年正式公布,具備兩大特點:

#1. 存在所有語言中的所有字符與數字的一一對應關系,即兼容萬國字符

#2. 與傳統的字符編碼的二進制數都有對應關系,詳解如下

很多地方或老的系統、應用軟件仍會采用各種各樣傳統的編碼,這是歷史遺留問題。此處需要強調:軟件是存放於硬盤的,而運行軟件是要將軟件加載到內存的,面對硬盤中存放的各種傳統編碼的軟件,想讓我們的計算機能夠將它們全都正常運行而不出現亂碼,內存中必須有一種兼容萬國的編碼,並且該編碼需要與其他編碼有相對應的映射/轉換關系,這就是unicode的第二大特點產生的緣由

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222437586-1811406226.jpg)

文本編輯器輸入任何字符都是最新存在於內存中,是unicode編碼的,存放於硬盤中,則可以轉換成任意其他編碼,只要該編碼可以支持相應的字符

# 英文字符可以被ASCII識別
英文字符--->unciode格式的數字--->ASCII格式的數字

# 中文字符、英文字符可以被GBK識別
中文字符、英文字符--->unicode格式的數字--->gbk格式的數字

# 日文字符、英文字符可以被shift-JIS識別
日文字符、英文字符--->unicode格式的數字--->shift-JIS格式的數字
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222608757-2073113168.jpg)

3.3 編碼與解碼

由字符轉換成內存中的unicode,以及由unicode轉換成其他編碼的過程,都稱為編碼encode

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222624101-836973470.jpg)

由內存中的unicode轉換成字符,以及由其他編碼轉換成unicode的過程,都稱為解碼decode

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222645169-200576606.jpg)

在諸多文件類型中,只有文本文件的內存是由字符組成的,因而文本文件的存取也涉及到字符編碼的問題

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191012121119664-283084630.png)

3.4 utf-8的由來

注意:如果保存到硬盤的是GBK格式二進制,當初用戶輸入的字符只能是中文或英文,同理如果保存到硬盤的是Shift_JIS格式二進制,當初用戶輸入的字符只能是日文或英文……如果我們輸入的字符中包含多國字符,那么該如何處理?

#多國字符—√—》內存(unicode格式的二進制)——X—》硬盤(GBK格式的二進制)

#多國字符—√—》內存(unicode格式的二進制)——X—》硬盤(Shift_JIS格式的二進制)

#多國字符—√—》內存(unicode格式的二進制)——√—》硬盤(???格式的二進制)

理論上是可以將內存中unicode格式的二進制直接存放於硬盤中的,但由於unicode固定使用兩個字節來存儲一個字符,如果多國字符中包含大量的英文字符時,使用unicode格式存放會額外占用一倍空間(英文字符其實只需要用一個字節存放即可),然而空間占用並不是最致命的問題,最致命地是當我們由內存寫入硬盤時會額外耗費一倍的時間,所以將內存中的unicode二進制寫入硬盤或者基於網絡傳輸時必須將其轉換成一種精簡的格式,這種格式即utf-8(全稱Unicode Transformation Format,即unicode的轉換格式)

# 多國字符—√—》內存(unicode格式的二進制)——√—》硬盤(utf-8格式的二進制)
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222752344-1754583023.jpg)

那為何在內存中不直接使用utf-8呢?

utf-8是不定長的:一個英文字符占1Bytes,一個中文字符占3Bytes,生僻字用更多的Bytes存儲

也就意味着如果用戶輸入的字符是:你y好,在內存中需要先經歷計算的過程:“你”應該用3Bytes,“y”應該用1Bytes,“好”應該用3Bytes,然后才能存儲,所以內存中如果直接使用utf-8格式去存儲字符,耗費的總時間=計算時間+存儲時間,而內存中使用定長的unicode格式存儲字符,就省去了計算時間,所以內存中使用unicode來存儲字符會浪費空間,但是會提升速度,這是一種用空間換時間的方法
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222812972-984653854.jpg)

四 字符編碼的應用

我們學習字符編碼就是為了存取字符時不發生亂碼問題:

#1、內存中固定使用unicode無論輸入任何字符都不會發生亂碼

#2、我們能夠修改的是存/取硬盤的編碼方式,如果編碼設置不正確將會出現亂碼問題。亂碼問題分為兩種:存亂了,讀亂了

#2.1 存亂了:如果用戶輸入的內容中包含中文和日文字符,如果單純以shift_JIS存,日文可以正常寫入硬盤,而由於中文字符在shift_jis中沒有找到對應關系而導致存亂了

#2.2 讀亂了:如果硬盤中的數據是shift_JIS格式存儲的,采GBK格式讀入內存就讀亂了

總結:

#1. 保證存的時候不亂:在由內存寫入硬盤時,必須將編碼格式設置為支持所輸入字符的編碼格式
#2. 保證存的時候不亂:在由硬盤讀入內存時,必須采用與寫入硬盤時相同的編碼格式

4.1 文本編輯器nodpad++存取文本文件

文本編輯器存取的都是文本文件,而文本文件中包含的內容全為字符,所以存取文本文件都涉及到字符編碼的問題。

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222835197-1302065297.png)
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222854232-665144298.png)
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222918086-482368213.png)
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222923152-1998608971.png)

4.2 python解釋器執行文件的前兩個階段

執行py文件的前兩個階段就是python解釋器讀文本文件的過程,與文本編輯讀文本文件的前兩個階段沒人任何區別,要保證讀不亂碼,則必須將python解釋器讀文件時采用的編碼方式設置為文件當初寫入硬盤時的編碼格式,如果沒有設置,python解釋器則才用默認的編碼方式,在python3中默認為utf-8,在python2中默認為ASCII,我們可以通過指定文件頭來修改默認的編碼

  • 在文件首行寫入包含#號在內的以下內容
# coding: 當初文件寫入硬盤時采用的編碼格式

解釋器會先用默認的編碼方式讀取文件的首行內容,由於首行是純英文組成,而任何編碼方式都可以識別英文字符。

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009222945270-1862079715.jpg)

4.3 python解釋器執行文件的第三個階段

設置文件頭的作用是保證運行python程序的前兩個階段不亂碼,經過前兩個階段后py文件的內容都會以unicode格式存放於內存中。

在經歷第三個階段時開始識別python語法,當遇到特定的語法name = '上'(代碼本身也都全都是unicode格式存的)時,需要申請內存空間來存儲字符串'上',這就又涉及到應該以什么編碼存儲‘上’的問題了。

在Python3中,字符串類的值都是使用unicode格式來存儲

由於Python2的盛行是早於unicode的,因此在Python2中是按照文件頭指定的編碼來存儲字符串類型的值的(如果文件頭中沒有指定編碼,那么解釋器會按照它自己默認的編碼方式來存儲‘上’),所以,這就有可能導致亂碼問題

![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009223006501-650304429.jpg)
# coding:utf-8
x = '上' # x的值為untf-8格式的二進制
print(x) # 打印操作是將x的值,即utf-8格式的二進制交給終端,當終端收到后發現並不是unicode(只有unicode才與字符有對應關系),所以終端會執行操作:utf-8二進制---解碼-->unicode格式的二進制,解碼的過程終端會采用自己默認的編碼,而在pycharm的終端默認編碼為utf-8、windows下的cmd終端的默認編碼為gbk,所以該打印操作在pycharm中顯示正常,而在windows下的cmd中則亂碼

# 在windows下的cmd中運行效果如下
C:\Users\Administrator>python2 E:\aaa.py
涓

python2后推出了一種補救措施,就是在字符串類型前加u,則會將字符串類型強制存儲unicode,這就與python3保持一致了,對於unicode格式無論丟給任何終端進行打印,都可以直接對應字符不會出現亂碼問題

# coding:utf-8
x = u'上' # 即便文件頭為utf-8,x的值依然存成unicode 
![](https://img2018.cnblogs.com/blog/1825659/201910/1825659-20191009223018368-1068079387.jpg)

4.4 字符串encode編碼與decode解碼的使用

# 1、unicode格式------編碼encode-------->其它編碼格式
>>> x='上' # 在python3在'上'被存成unicode
>>> res=x.encode('utf-8')
>>> res,type(res) # unicode編碼成了utf-8格式,而編碼的結果為bytes類型,可以當作直接當作二進制去使用
(b'\xe4\xb8\x8a', <class 'bytes'>)

# 2、其它編碼格式------解碼decode-------->unicode格式
>>> res.decode('utf-8') 
'上'


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM