概述
在使用Python或者其他的編程語言,都會多多少少遇到編碼錯誤,處理起來非常痛苦,在Stack Overflow和其他的編程問答網站上,UnicodeDecodeError和UnicodeEncodeError也經常被提及,本篇教程希望能幫你認識Python編碼,并能夠從容的處理編碼問題,
這里也要注意:不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!
本教程提到的編碼知識并不限定在Python,其他語言也大同小異,但我們依然會以Python為主,來演示和講解編碼知識,
通過該教程,你將學習到如下的知識:
- 獲取有關字符編碼和數字系統的概念
- 理解編碼如何使用Python的str和bytes
- 通過int函式了解Python對數字系統的支持
- 熟悉Python字符編碼和數字系統相關的內置函式
什么是字符編碼
現在的編碼規則已經有好多了,最簡單、最基本是的ASCII編碼,只要是你學過計算機相關的課程,你就應該多少了解一點ASCII編碼,他是最小也是最適合了解字符編碼原理的編碼規則,具體如下:
- 小寫英文字符:a-z
- 大寫英文字符:A-Z
- 符號: 比如 $和!
- 空白符:回車、換行、空格等
- 一些不可列印的字符: 比如\b等
那么,字符編碼的定義到底是什么了?它是一種將字符(如字母,標點符號,符號,空格和控制字符)轉換為整數并最終轉換為bit進行存盤的方法, 每個字符都可以編碼為唯一的bit序列, 如果你對bit的概念不了解,請不要擔心,我們后面會介紹,
ASCII碼的字符被分為如下幾組:
ASCII表一共包括128個字符,如果你想了解整個ASCII表,這里有
Python string模塊
string模塊是python里處理字串很方便的模塊,它包括了整個ASCII字符,讓我們來看看部分string模塊原始碼:
1
|
# From lib/python3.7/string.py
|
你可以在Python中這樣使用string模塊:
1
|
>>> import string
|
什么是bit
學過計算機相關課程的同學,應該都知道,bit是計算機內部存盤單位,只有0和1兩個狀態(二進制),我們上面所說的ASCII表,都是一個10進制的數字表示一個字符,而這個10進制數字,最侄訓轉換成0和1,存盤在計算機內部,例如(第一列是10進制數字,第二列是二進制,第三列是計算機內部存盤結果):
這是一種在Python中將ASCII字串表示為位序列的方便方法, ASCII字串中的每個字符都被偽編碼為8位,8位序列之間有空格,每個字符代表一個字符:
1
|
>>> def make_bitseq(s: str) -> str:
|
我們也可以是用python的f-string 來格式化,比如f”{ord(i):08b}”:
-
冒號的左側是ord(i),它是實際的物件,其值將被格式化并插入到輸出中, 使用ord()為單個str字符提供了base-10代碼點,
-
冒號的右側是格式說明符, 08表示寬度為8,0填充,b用作在基數2(二進制)中輸出結果數的符號,
ASCII編碼不夠用了
ASCII采用的是8bit來存盤字符(只使用7位,剩下的1位二進制為0),所以,ASCII最多存盤128個字符,這有個簡單的公式,計算存盤字符的bit數量與存盤字符總數的關系:2的n次方,n表示bit數量,例如:
- 1bit存盤2個字符
- 8bit存盤256個字符
- 64bit存盤2的64次方 == 18,446,744,073,709,551,616
我們可以寫個簡單的代碼,來計算一下,指定字符數量,至少需要多少bit來存盤:
1
|
>>> from math import ceil, log
|
數字系統
在上面的ASCII討論中,您看到每個字符映射到0到127范圍內的整數,但在CPython中還有其他的數字系統,通過其他方式是表示數字,除了十進制外,python還支持以下幾個方式:
- Binary: 2進制
- Octal: 8進制
- Hexadecimal (hex): 16進制
你可能要問,為什么有了十進制,還要支持這么多其他進制的數字了?這個取決你的業務場景和作業系統,在Python里,把str轉換成int,默認是10進制的,
1
|
>>> int('11')
|
你可以在賦值時,直接告訴解釋器數字的型別,不同進制標表示方法如下:
| 型別 | 前綴 | 示例 |
|---|---|---|
| n/a | n/a | 11 |
| 二進制 | 0b 或者 0B | 0b11 |
| 八進制 | 0o 或者 0O | 0o11 |
| 十六進制 | 0x 或者 0X | 0x11 |
1
|
>>> 11
|
深入Unicode
正如您所看到的,ASCII的問題在于它不是一個足夠大的字符集來容納世界上的語言,方言,符號和字形, (這對于英語來說甚至都不夠大,)Unicode從根本上起到與ASCII相同的作用,但是Unicode擁有更大的存盤空間,具有1,114,112個可能的字符,能夠完全包含世界上所有的語言,事實上,ASCII是Unicode的完美子集, Unicode表中的前128個字符與您合理期望的ASCII字符完全對應,
Unicode本身不是編碼,但是有很多遵循Unicode編碼規范編碼,后面講到的UTF-8就是其中一個,
Unicode vs UTF-8
Unicode是一種抽象編碼標準,而不是編碼,這就是UTF-8和其他編碼方案發揮作用的地方, Unicode標準(字符到代碼點的映射)從其單個字符集定義了幾種不同的編碼,UTF-8及其較少使用的表兄弟UTF-16和UTF-32是用于將Unicode字符表示為每個字符一個或多個位元組的二進制資料的編碼格式,我們稍后將討論UTF-16和UTF-32,但到目前為止,UTF-8占據了最大份額,
Python 3里的編碼與解碼
Python 3的str型別用于表示人類可讀的文本,可以包含任何Unicode字符,
相反,位元組型別表示二進制資料或原始位元組序列,它們本質上沒有附加編碼,
編碼和解碼是從一個到另一個的程序:
decode 和 encode 函式,默認編碼是utf-8:
1
|
>>> "résumé".encode("utf-8")
|
str.encode()的結果是一個bytes物件,bytes物件只允許ASCII字符,這就是為什么在呼叫“ElNi?o”.encode(“utf-8”)時,允許ASCII兼容的“El”按原樣表示,但帶有波浪號的n被轉義為“\ xc3 \ xb1”, 這個看起來很亂的序列代表兩個位元組,十六進制為0xc3和0xb1:
1
|
>>> " ".join(f"{i:08b}" for i in (0xc3, 0xb1))
|
Python3一切字符皆Unicode
-
默認情況下,Python 3源代碼假定為UTF-8, 這意味著您不需要# - - 編碼:UTF-8 - - 位于Python 3中.py檔案的頂部,
-
默認情況下,所有文本(str)都是Unicode, 編碼的Unicode文本表示為二進制資料(位元組), str型別可以包含任何文字Unicode字符,例如“Δv/Δt”,所有這些字符都將存盤為Unicode,
-
Unicode字符集中的任何內容都是識別符號中的猶太符號,這意味著résumé=“?/ Documents / resume.pdf”是有效的,雖然這看起來很花哨,
-
Python的re模塊默認為re.UNICODE標志而不是re.ASCII, 這意味著,例如,r“\ w”匹配Unicode字符,而不僅僅是ASCII字母,
-
str.encode()和bytes.decode()中的默認編碼是UTF-8,
還有一個更細微的屬性,即內置的open()的默認編碼是依賴于平臺的,并且取決于locale.getpreferredencoding()的值:
1
|
>>> # Mac OS X High Sierra
|
一個關鍵特性是UTF-8是一種可變長度編碼,回想一下關于ASCII的部分, 擴展ASCII-land中的所有內容最多需要一個位元組的空間, 您可以使用以下生成器運算式快速證明這一點:
1
|
>>> all(len(chr(i).encode("ascii")) == 1 for i in range(128))
|
UTF-8完全不同, 給定的Unicode字符可以占用1到4個位元組, 以下是占用四個位元組的單個Unicode字符的示例:
1
|
>>> ibrow = "??"
|
這是len()的一個微妙但重要的特性:
- 作為Python str的單個Unicode字符的長度始終為1,無論它占用多少位元組,
- 編碼為位元組的相同字符的長度將介于1和4之間,
UTF-16和UTF-32
我們來聊聊UTF-16和UTF-32,在實際的編程實踐中,它們和UTF-8區別還是很重要的,下面的通過實體我們來看看具體區別:
1
|
>>> letters = "αβγδ"
|
在這種情況下,使用UTF-8編碼四個希臘字母然后解碼回UTF-16中的文本將產生一個完全不同語言(韓語)的文本str,
此表匯總了UTF-8,UTF-16和UTF-32下的位元組范圍或位元組數:
| 編碼 | 長度(位元組) | 是否可變 |
|---|---|---|
| UTF-8 | 1~4 | 是 |
| UTF-16 | 2~4 | 是 |
| UTF-32 | 4 | 否 |
UTF系列編碼另外一個需要注意的地方是,UTF-8編碼占用存盤空間不一定比UTF-16少,因為他們都不是固定長度的,例如
1
|
>>> text = "記者 鄭啟源 羅智堅"
|
原因是U + 0800到U + FFFF(十進制的2048到65535)范圍內的代碼點占用了UTF-8中的三個位元組,而UTF-16中僅占用了兩個位元組,
正常情況下,最好不用使用UTF-16,除非特殊要求,不然UTF-8更加通用,
Python內建函式
Python內置了很多與編碼相關的函式:
- ascii()
- bin()
- bytes()
- chr()
- hex()
- int()
- oct()
- ord()
- str()
可以分成以下幾組:
- ascii(),bin(),hex()和oct(), 第一個是ascii(),它生成一個僅物件的ASCII表示,其中非ASCII字符被轉義, 其余三個分別給出整數的二進制,十六進制和八進制表示,
- bytes(),str()和int()是各自型別,bytes,str和int的類建構式, 它們各自提供了將輸入強制轉換為所需型別的方法, 例如,如前所述,雖然int(11.0)可能更常見,但您可能也會看到int(‘11’,base = 16),
- ord()和chr(),ord()將str字符轉換為10進制,而chr()執行相反的操作,
Python中的其他編碼
目前,我們講了4中編碼:
- ASCII
- UTF-8
- UTF-16
- UTF-32
還有其他很多編碼,比如Latin-1(也稱作ISO-8859-1),這是HTTP默認的編碼,然而windows是Latin-1變體,稱作cp1252,
完整的已接受編碼串列隱藏在編解碼器模塊的檔案中,該模塊是Python標準庫的一部分,
還有一個有用的公認編碼需要注意,即“unicode-escape”, 如果您有一個已解碼的str并希望快速獲得其轉義的Unicode文字的表示,那么您可以在.encode()中指定此編碼:
1
|
>>> alef = chr(1575) # Or "\u0627"
|
注意外部資料編碼
雖然Python代碼默認使用了UTF-8作為編碼,但并不意味著外部輸入的資料也是UTF-8編碼的,如果這些外部的資料沒有指定編碼,那么在處理他們是,你就要格外小心了,比如你呼叫API獲取資料,正常是用UTF-8去解碼,沒有問題,但如果突然API給你回傳這樣的資料:
1
|
>>> data = https://www.cnblogs.com/chengxuyuanaa/p/b"\xbc cup of flour"
|
這地方拋出了UnicodeDecodeError錯誤,仔細檢查,發現其實資料的編碼是Latin-1,
1
|
>>> data.decode("latin-1")
|
如果你對字串的編碼不確定,可以使用chardet庫來檢查字串編碼,
總結注意:不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!
在本文中你已經了解了編碼的詳細原理,相信你在以后的編程程序中,再遇到編碼錯誤,相信你能比較從容的解決了,
本文的文字及圖片來源于網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/152390.html
標籤:Python
上一篇:【Django 2.2檔案系列】Model 外鍵中的on_delete引數用法
下一篇:selenium + geckodriver報 Message: Unable to find a matching set of capabilities
