1. 程式人生 > >公司Python大佬總結給新人的編碼原理,看完徹底明白Python編碼原理

公司Python大佬總結給新人的編碼原理,看完徹底明白Python編碼原理

世界觀 到你 世界 日文 har ror 公司 講解 了吧

關於編碼的歷史演變,utf-8是如何一步步發展來的,windows為啥依舊保持gbk的編碼。。。
等等這些問題,網上一搜一大堆,大部分都是轉發、分享後的雷同內容,依舊解決不了我內心的疑惑。。。
編碼是個蛋疼的事情,倘若不弄清楚, 怎麽在中國混?
經過自己查閱多方文檔、多次深入實驗,我樹立了對編碼的基本世界觀。

基礎內容請自行谷歌..廢話不多說,直接上幹貨!!

下面用幾個簡單的代碼段, 一步步講解編碼中“編”和“解”的問題!!(linux中運行)


“ 代碼 一 ”:

 1 import sys, locale
 2 
 3 s = "小甲"
 4 print(s)
 5 print(type(s))
6 print(sys.getdefaultencoding()) 7 print(locale.getdefaultlocale()) 8 9 with open("utf1","w",encoding = "utf-8") as f: 10 f.write(s) 11 with open("gbk1","w",encoding = "gbk") as f: 12 f.write(s) 13 with open("jis1","w",encoding = "shift-jis") as f: 14 f.write(s)

代碼很簡單,學過Python的人應該都能看懂是啥意思~~
我們看一下運行結果:

“ 代碼 一 ” 運行結果:

1 小甲
2 <class str>
3 utf-8
4 (en_US, UTF-8)

正如大家所想, 就是將“小甲”原樣打印出來, 再把“小甲”存到3個文件中。
(shift-jis是日文編碼格式)

這麽的代碼我還會不知道嗎? ( 重點在後面 )

這裏解釋一下打印出來的兩個“utf-8”是什麽意思:

上面的 utf-8 指:系統默認編碼

  • 註: 不要把系統以為是操作系統,這裏可以理解成python3的編譯器本身

下面的 utf-8 指:本地默認編碼

  • 註: 這個才是操作系統的編碼。(在Windows運行會變成gbk)

現在我們分別查看utf1 、gbk1、jis1 這三個文件的內容:

utf1 : 小甲
gbk1 : С???
jis1 : ???b

問題:
為什麽 utf1 的內容很清楚,沒有編碼問題,而gbk1 、jis1 的內容都出現了亂碼?

解釋:
因為我文件存儲時用的編碼格式不是utf-8,而此時讀取這兩個文件時,使用的是linux操作系統的默認編碼“utf-8”。
那麽寫入磁盤時不是用utf-8, 讀出時卻用utf-8,當然讀不出來了。
(這裏需要大家了解encoding的真實作用)

“ 代碼 二 ”:

 1 #coding=gbk
 2 import sys, locale
 3 
 4 s = "小甲"
 5 #coding=gbk
 6 import sys, locale
 7 
 8 s = "小甲"
 9 print(s)
10 print(type(s))
11 print(sys.getdefaultencoding())
12 print(locale.getdefaultlocale())
13 
14 with open("utf2","w",encoding = "utf-8") as f:
15     f.write(s)
16 with open("gbk2","w",encoding = "gbk") as f:
17     f.write(s)
18 with open("jis2","w",encoding = "shift-jis") as f:
19     f.write(s)

代碼結結構一樣很簡單
但是請大家註意: 我在頭部加了某個編碼聲明
在代碼運行前, 請大家自行猜測結果~~~

“ 代碼 二 ” 運行結果 :

1 灝忕敳
2 <class str>
3 utf-8
4 (en_US, UTF-8)
5 Traceback (most recent call last):
6   File "2", line 15, in <module>
7     f.write(s)
8 UnicodeEncodeError: shift_jis codec cant encode character \u704f in position 0: illegal multibyte sequence

問題來了:

1、代碼中明明 s = “小甲”, 為什麽變成了 “灝忕敳” ??
2、為什麽 jis 的編碼失敗了?(之前頂多只出現了亂碼的問題,還不會報錯,那它內部到底發生了什麽?)
3、“coding=gbk” 到底是什麽意思??
4、我明明寫了 “coding=gbk” 的編碼聲明,為什麽系統編碼、本地默認編碼還是沒有改變?(那我寫了有啥用?)


解釋一下:

以上這麽多問題, 主要是因為沒搞清楚頭文件的 “coding=gbk” 編碼聲明是什麽意思!!

1、它的意思是python3編譯器在讀取該.py文件時候,我應該用什麽格式將它 “解碼”?只和讀取有關,所以當你確定你代碼編輯時候用的是什麽格式編碼的,你才能把相應的編碼格式寫入頭文件。
(在此示範代碼中,我用的是linux的默認編碼編輯,也就是utf-8,那麽在後面運行的時候,卻要求解釋器用gbk去解碼,自然很過分,就會出現了s=“小甲” 亂碼的問題)

(大家一定要知道,編碼是 “編” 和 “解” 的兩個步驟,一定要一一對應才能正確解碼!雖然通常我們都叫“編碼格式”,這是有一定誤導性的。
實際上另一半是“解碼格式”,要有意識地區分 “編” 和 “解” ,我們不能像網上有些文章一樣將這兩者混為一談!!)

2、根據上面的解釋應該可以明白,寫了它之後,並不會更改本地、系統默認編碼。
(本地默認編碼只跟操作系統相關,linux中是utf-8,windows中是gbk。)
(系統默認編碼實際是有python3和python2的差異的,python3是utf-8,python2是ascii。)

3、那麽,上面兩種編碼的作用體現在哪裏呢?

敲黑板,劃重點:

系統默認編碼 指:
在python3編譯器讀取.py文件時,若沒有頭文件編碼聲明,則默認使用“utf-8”來對.py文件進行解碼。並且在調用 encode()這個函數時,不傳參的話默認是“ utf-8 ”。(這與下面的open( )函數中的“encoding”參數要做區分,非常誤導人!!!)

本地默認編碼 指:
在你編寫的python3程序時,若使用了 open( )函數 ,而不給它傳入 “ encoding ” 這個參數,那麽會自動使用本地默認編碼。沒錯,如果在Windows系統中,就是默認用gbk格式!!!

(這個問題困擾了我好久, 不說好了一直默認utf-8到天長地久的嘛,咋換成win後就頻頻失信呢。所以請大家在這裏註意:linux中可以不用傳“ encoding” 的參數, 而win中不能忘了~~~)

4、再來回答一下報錯的問題:
因為我們的編譯器已經用了gbk來解碼此.py文件了,所以讀取出來的變量 s 已經變成了我們現在看到的“ 灝忕敳 ” 了!那麽此時把 s 存到磁盤文件中,實際上存的是亂碼後的 “ 灝忕敳 ”。而在日文中,是沒有這3個字的, 所以自然反饋說 “ 在 position 0 的位置,編碼失敗了”

現在我們再來分別查看utf2 、gbk2、jis2 這三個文件的內容:

utf2 : 灝忕敳
gbk2 : 小甲
jis2 :

(跟你想象中的結果是否一樣呢??嘿嘿嘿~~)

問題:

1、為什麽 我用 “utf-8 ” 去編碼存儲,後來用linux默認的 “ utf-8 ” 去解碼,卻出現亂碼?
2、為什麽我用“ gbk ” 去編碼存儲, 後面用linux默認的 “ utf-8 ” 去解碼,明明編碼、解碼格式不一致,卻能夠正常顯示?

解釋:

1、實際上面兩個問題是同一個問題,相信細心的同學已經知道問題出在哪裏了,我上文已經說的很清楚了。此時的變量 s 已經變成了“ 灝忕敳 ”, 那麽utf2這個文本文件自然是顯示“灝忕敳”。

2、而“灝忕敳”這三個字符是怎麽來的呢?

第1步:  小甲(unicode)   ---用 "utf-8" 編碼--->    e5b0 8fe7 94b2 (utf-8編碼後的二進制代碼)

第2步:  e5b0 8fe7 94b2   ---用 “gbk” 解碼--->     " 灝忕敳 " (unicode)(亂碼)

第3步:  “ 灝忕敳 ”     --- 用 “ gbk ” 編碼--->     e5b0 8fe7 94b2 ( 第2步的逆向)

第4步:  e5b0 8fe7 94b2     ---用 “ utf-8 ” 解碼--->    小甲(unicode) 

我想上述的步驟夠清楚了吧 ~
第3、 4 步就是逆推回去,就變成了正常的 “ 小甲 ”
看懂了這個 “ 編碼 ” 和 “ 解碼 ” 的過程,你的編碼問題已經解決大半了!

“ 代碼 三 ”:

#coding=shift-jis
import sys, locale

s = "小甲"
print(s)
print(type(s))
print(sys.getdefaultencoding())
print(locale.getdefaultlocale(), "\n\n")

a = s.encode("shift-jis")
print(a)
print(type(a))
b = a.decode("utf-8")
print(b)
print(type(b))
print(a.decode("gbk"))

with open("utf3","w",encoding = "utf-8") as f:
    f.write(s)
with open("gbk3","w",encoding = "gbk") as f:
    f.write(s)
with open("jis3","w",encoding = "shift-jis") as f:
    f.write(s)

#Python學習群548377875

代碼整體結構還是老樣子,只不過中間多加了一小段代碼,便於解釋~

“ 代碼 三 ” 運行結果 :

蟆冗抜
<class str>
utf-8
(en_US, UTF-8)


b\xe5\xb0\x8f\xe7\x94\xb2
<class bytes>
小甲
<class str>
灝忕敳

這裏可以看到,此時我們的變量 s 已經變成了“ 蟆冗抜 ”(另一個用jis解碼造成的亂碼)。

那麽此時,我把 “ 蟆冗抜 ” 用 “ shift-jis ” 解碼回去並賦值給變量 a,打印一下,可以看到 a 就是正常顯示的 “ 小甲 ”, 這也證明了我上面的推斷是絕對正確的!!

現在,我們依舊分別查看一下 utf3 、gbk3、jis3 這三個文件的內容:

utf3 : 蟆冗抜
gbk3 : ???i
jis3 : 小甲

(oops~~ 見鬼,又是這麽亂七八糟的東西)

這裏我澄清一下,實際上utf3這個至少還能有文字,這叫亂碼。而gbk3那個東西一團黑是什麽鬼,是報錯,linux的默認編碼無法解碼gbk3的文件,所以打印地亂七八糟。

問題:

  • 為什麽 utf3 的文件是顯示亂碼, 而 gbk3 的文件卻是報錯呢??

解釋:

  • 這是因為 utf-8 與 gbk 編碼的算法差異。
  • 我們最常看到的是utf-8解碼報錯,因為它是可變長的的編碼,有1個字節的英文字符,也有2個字節的阿拉伯文,也有3個字節的中文和日文。
  • gbk對英文是使用單字節編碼(也就意味著兼容ascii),而gbk對中文部分是采取定長的2字節,總體編碼範圍為 8140-FEFE,首字節在 81-FE 之間,尾字節在 40-FE 之間。所以說它只要沒有碰到尾字節在40之內的字符,都會一股腦地按照2字節去解碼成中文。而中文在 utf-8 編碼後,一般是三字節的。當解碼的字節數和編碼的字節數不匹配時,自然會造成全是亂碼的局面。

(此處感謝 “流放的國王” 的指正~~)

  • 而utf-8是有嚴格定義的,一個字節的字符高位必須是0;三個字節的字符中,第一個字節的高位是1110開頭。
  • (相關utf-8的編碼算法鏈接)

至此,代碼的示範部分就結束了~~ 碼字碼得我手酸 ~~~~(>_<)~~~~


最後,

tips:

1、所有文件的編碼格式都由你當下使用的編輯器決定的!!在windows中編輯的文本放在瀏覽器解析顯示的時候,有時亂碼,有時又正常,這是由於windows中很多文本編輯器默認使用和操作系統一致的編碼格式。
所以在文本存儲前,一定要搞清楚我們用的是utf-8還是gbk!!!
而當你使用Python的 open( ) 函數時,是內存中的進程與磁盤的交互,而這個交互過程中的編碼格式則是使用操作系統的默認編碼(Linux為utf-8,windows為gbk)

2、相信學Python的同學們經常會聽到,python3 的默認編碼是utf-8。而有的時候,又有人說python3 的默認編碼是unicode,那麽是不是會有人跟我初學時候一樣傻傻分不清楚這兩者的關系呢?

  • 實際上unicode就是一個字符集,一個字符與數字一一對應的映射關系,因為它一律以2個字節編碼(或者也有4個字節的,這裏不討論),所以占用空間會大一些,一般只用於內存中的編碼使用。
  • 而 utf-8 是為了實現unicode 的傳輸和存儲的。因為它可變長,存英文時候可以節省大量存儲空間。傳輸時候也節省流量,所以更加 “ international ”~

所以說,上述兩種說法沒有歧義,進程在內存中的表現是“ unicode ”的編碼;當python3編譯器讀取磁盤上的.py文件時,是默認使用“utf-8”的;當進程中出現open(), write() 這樣的存儲代碼時,需要與磁盤進行存儲交互時,則是默認使用操作系統的默認編碼。


我也不知道如何才能成為一條 “ 華麗 ” 的分割線~~~


打字、排版、整理思路花了近5個小時,若是這篇文章有幫助到你、有給你帶來一些對編碼的新靈感,希望可以點個贊。

比心~ ?????????????????????????

公司Python大佬總結給新人的編碼原理,看完徹底明白Python編碼原理