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

来源:https://www.cnblogs.com/huohuohuo1/archive/2018/08/17/9495544.html
-Advertisement-
Play Games

基礎內容請自行谷歌..廢話不多說,直接上乾貨!! “ 代碼 一 ”: 1 import sys, locale 2 3 s = "小甲" 4 print(s) 5 print(type(s)) 6 print(sys.getdefaultencoding()) 7 print(locale.getd ...


關於編碼的歷史演變,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 can't 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個小時,若是這篇文章有幫助到你、有給你帶來一些對編碼的新靈感,希望可以點個贊。

比心~ ❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我在知乎上看到一個形象解釋的例子: 個人理解:將具體業務和底層邏輯解耦的組件。 大致的效果是:需要利用服務的人(前端寫業務的),不需要知道底層邏輯(提供服務的)的具體實現,只要拿著中間件結果來用就好了。 舉個例子:我開了一家炸雞店(業務端),然而周邊有太多屠雞場(底層),為了成本我肯定想一個個比價, ...
  • 前幾天和同事聊天,同事說: “業務的服務(相對於我們基礎架構這邊的底層技術)在技術上就需要解決三個問題:分散式、通信和存儲。” 我回憶之前做業務的時光,覺得確實,再加上一個“服務治理”就差不多了。想想“服務設計要解決的問題”這個話題可以把之前靜兒寫的很多文章做一個歸納概括。今天做一個總結。 分散式 ...
  • 適配器模式在軟體開發界使用及其廣泛,在工業界,現實中也是屢見不鮮。比如手機充電器,筆記本充電器,廣播接收器,電視接收器等等。都是適配器。 適配器主要作用是讓本來不相容的兩個事物相容和諧的一起工作。比如, 通常我們使用的交流電都是220v,但是手機電池能夠承載的5v電壓,因此直接將我們使用的220v交 ...
  • 這段時間接觸公眾號開發,寫下向用戶發送消息模板的介面調用 先上介面代碼 15行那段getJsonByWX是統一調用微信介面的方法,每個項目都有自己的調用方法,我這裡就不貼了。介面調用鏈接:https://api.weixin.qq.com/cgi-bin/message/template/send? ...
  • 主要思路參考這篇博客的內容,把地址貼上: http://www.cnblogs.com/zhoujie/p/python18.html 下麵貼上我自己的代碼 讀取excel數據的demo代碼如下: 1 def read_excel_demo(): 2 # 打開文件 3 workbook = xlrd ...
  • const的用法,特別是用在函數後面 在普通的非 const成員函數中,this的類型是一個指向類類型的 const指針。可以改變this所指向的值,但不能改變 this所保存的地址。 在 const成員函數中,this的類型是一個指向 const類類型對象的 const指針。既不能改變 this所... ...
  • 常用排序 名稱 複雜度 說明 備註 冒泡排序Bubble Sort O(N*N) 將待排序的元素看作是豎著排列的“氣泡”,較小的元素比較輕,從而要往上浮 插入排序 Insertion sort O(N*N) 逐一取出元素,在已經排序的元素序列中從後向前掃描,放到適當的位置 起初,已經排序的元素序列為 ...
  • 首先確定要爬取的笑話網站,我是直接百度笑話搜到的第一個網站,網址是http://www.jokeji.cn/,點進去發現網頁構建在我看來還是比較複雜的,由於還是初學者,首先得先找到網頁資源集中所在,找出其中的規律,然後才好有針對性的爬取資源。對於這個網址,我發現在左邊側欄有一個笑話大全的分類框。這個 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...