Python3學習總結,從基礎的字符串到函數

概述

在使用Python或者其他的編程語言,都會多多少少遇到編碼錯誤,處理起來非常痛苦。在Stack Overflow和其他的編程問答網站上,UnicodeDecodeError和UnicodeEncodeError也經常被提及。本篇教程希望能幫你認識Python編碼,並能夠從容的處理編碼問題。

本教程提到的編碼知識並不限定在Python,其他語言也大同小異,但我們依然會以Python爲主,來演示和講解編碼知識。

通過該教程,你將學習到如下的知識:

  • 獲取有關字符編碼和數字系統的概念
  • 理解編碼如何使用Python的str和bytes
  • 通過int函數了解Python對數字系統的支持
  • 熟悉Python字符編碼和數字系統相關的內置函數

什麼是字符編碼

現在的編碼規則已經有好多了,最簡單、最基本是的ASCII編碼,只要是你學過計算機相關的課程,你就應該多少了解一點ASCII編碼,他是最小也是最適合瞭解字符編碼原理的編碼規則。具體如下:

  • 小寫英文字符:a-z
  • 大寫英文字符:A-Z
  • 符號: 比如 $和!
  • 空白符:回車、換行、空格等
  • 一些不可打印的字符: 比如\b等

那麼,字符編碼的定義到底是什麼了?它是一種將字符(如字母,標點符號,符號,空格和控制字符)轉換爲整數並最終轉換爲bit進行存儲的方法。 每個字符都可以編碼爲唯一的bit序列。 如果你對bit的概念不瞭解,請不要擔心,我們後面會介紹。

ASCII碼的字符被分爲如下幾組:

ASCII表一共包括128個字符,如果你想了解整個ASCII表,這裏有

如果你依然在編程的世界裏迷茫,可以加入我們的Python學習扣qun:784758214,看看前輩們是如何學習的!交流經驗!自己是一名高級python開發工程師,從基礎的python腳本到web開發、爬蟲、django、數據挖掘等,零基礎到項目實戰的資料都有整理。送給每一位python的小夥伴!分享一些學習的方法和需要注意的小細節,點擊加入我們的 python學習者聚集地

Python string模塊

string模塊是python裏處理字符串很方便的模塊,它包括了整個ASCII字符,讓我們來看看部分string模塊源碼:

# From lib/python3.7/string.py

whitespace = ' \t\n\r\v\f'
ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ascii_letters = ascii_lowercase + ascii_uppercase
digits = '0123456789'
hexdigits = digits + 'abcdef' + 'ABCDEF'
octdigits = '01234567'
punctuation = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
printable = digits + ascii_letters + punctuation + whitespace

你可以在Python中這樣使用string模塊:

>>> import string

>>> s = "What's wrong with ASCII?!?!?"
>>> s.rstrip(string.punctuation)
'What's wrong with ASCII'

什麼是bit

學過計算機相關課程的同學,應該都知道,bit是計算機內部存儲單位,只有0和1兩個狀態(二進制),我們上面所說的ASCII表,都是一個10進制的數字表示一個字符,而這個10進制數字,最終會轉換成0和1,存儲在計算機內部。例如(第一列是10進制數字,第二列是二進制,第三列是計算機內部存儲結果):

這是一種在Python中將ASCII字符串表示爲位序列的方便方法。 ASCII字符串中的每個字符都被僞編碼爲8位,8位序列之間有空格,每個字符代表一個字符:

>>> def make_bitseq(s: str) -> str:
...     if not s.isascii():
...         raise ValueError("ASCII only allowed")
...     return " ".join(f"{ord(i):08b}" for i in s)

>>> make_bitseq("bits")
'01100010 01101001 01110100 01110011'

>>> make_bitseq("CAPS")
'01000011 01000001 01010000 01010011'

>>> make_bitseq("$25.43")
'00100100 00110010 00110101 00101110 00110100 00110011'

>>> make_bitseq("~5")
'01111110 00110101'

我們也可以是用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來存儲:

>>> from math import ceil, log

>>> def n_bits_required(nvalues: int) -> int:
...     return ceil(log(nvalues) / log(2))

>>> n_bits_required(256)
8

數字系統

在上面的ASCII討論中,您看到每個字符映射到0到127範圍內的整數。但在CPython中還有其他的數字系統,通過其他方式是表示數字。除了十進制外,python還支持以下幾個方式:

  • Binary: 2進制
  • Octal: 8進制
  • Hexadecimal (hex): 16進制

你可能要問,爲什麼有了十進制,還要支持這麼多其他進制的數字了?這個取決你的業務場景和操作系統,在Python裏,把str轉換成int,默認是10進制的。

在學習過程中有什麼不懂得可以加我的
python學習交流扣扣qun,784758214
羣裏有不錯的學習視頻教程、開發工具與電子書籍。
與你分享python企業當下人才需求及怎麼從零基礎學習好python,和學習什麼內容
>>> int('11')
11
>>> int('11', base=10)  # 10 is already default
11
>>> int('11', base=2)  # Binary
3
>>> int('11', base=8)  # Octal
9
>>> int('11', base=16)  # Hex
17

你可以在賦值時,直接告訴解釋器數字的類型,不同進制標表示方法如下:

類型 前綴 示例
n/a n/a 11
二進制 0b 或者 0B 0b11
八進制 0o 或者 0O 0o11
十六進制 0x 或者 0X 0x11
>>> 11
11
>>> 0b11  # 二進制
3
>>> 0o11  # 八進制
9
>>> 0x11  # 16進制
17

深入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:

>>> "résumé".encode("utf-8")
b'r\xc3\xa9sum\xc3\xa9'
>>> "El Niño".encode("utf-8")
b'El Ni\xc3\xb1o'

>>> b"r\xc3\xa9sum\xc3\xa9".decode("utf-8")
'résumé'
>>> b"El Ni\xc3\xb1o".decode("utf-8")
'El Niño'

str.encode()的結果是一個bytes對象,bytes對象只允許ASCII字符。這就是爲什麼在調用“ElNiño”.encode(“utf-8”)時,允許ASCII兼容的“El”按原樣表示,但帶有波浪號的n被轉義爲“\ xc3 \ xb1”。 這個看起來很亂的序列代表兩個字節,十六進制爲0xc3和0xb1:

>>> " ".join(f"{i:08b}" for i in (0xc3, 0xb1))
'11000011 10110001'

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()的值:

>>> # Mac OS X High Sierra
>>> import locale
>>> locale.getpreferredencoding()
'UTF-8'

>>> # Windows Server 2012; other Windows builds may use UTF-16
>>> import locale
>>> locale.getpreferredencoding()
'cp1252'

一個關鍵特性是UTF-8是一種可變長度編碼。回想一下關於ASCII的部分。 擴展ASCII-land中的所有內容最多需要一個字節的空間。 您可以使用以下生成器表達式快速證明這一點:

>>> all(len(chr(i).encode("ascii")) == 1 for i in range(128))
True

UTF-8完全不同。 給定的Unicode字符可以佔用1到4個字節。 以下是佔用四個字節的單個Unicode字符的示例:

>>> ibrow = "?"
>>> len(ibrow)
1
>>> ibrow.encode("utf-8")
b'\xf0\x9f\xa4\xa8'
>>> len(ibrow.encode("utf-8"))
4

>>> # Calling list() on a bytes object gives you
>>> # the decimal value for each byte
>>> list(b'\xf0\x9f\xa4\xa8')
[240, 159, 164, 168]

這是len()的一個微妙但重要的特性:

  • 作爲Python str的單個Unicode字符的長度始終爲1,無論它佔用多少字節。
  • 編碼爲字節的相同字符的長度將介於1和4之間。

UTF-16和UTF-32

我們來聊聊UTF-16和UTF-32,在實際的編程實踐中,它們和UTF-8區別還是很重要的,下面的通過實例我們來看看具體區別:

>>> letters = "αβγδ"
>>> rawdata = letters.encode("utf-8")
>>> rawdata.decode("utf-8")
'αβγδ'
>>> rawdata.decode("utf-16")  # 
'뇎닎돎듎'

在這種情況下,使用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少,因爲他們都不是固定長度的。例如

在學習過程中有什麼不懂得可以加我的
python學習交流扣扣qun,784758214
羣裏有不錯的學習視頻教程、開發工具與電子書籍。
與你分享python企業當下人才需求及怎麼從零基礎學習好python,和學習什麼內容
>>> text = "記者 鄭啟源 羅智堅"
>>> len(text.encode("utf-8"))
26
>>> len(text.encode("utf-16"))
22

原因是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()中指定此編碼:

>>> alef = chr(1575)  # Or "\u0627"
>>> alef_hamza = chr(1571)  # Or "\u0623"
>>> alef, alef_hamza
('ا', 'أ')
>>> alef.encode("unicode-escape")
b'\\u0627'
>>> alef_hamza.encode("unicode-escape")
b'\\u0623'

注意外部數據編碼

雖然Python代碼默認使用了UTF-8作爲編碼,但並不意味着外部輸入的數據也是UTF-8編碼的,如果這些外部的數據沒有指定編碼,那麼在處理他們是,你就要格外小心了。比如你調用API獲取數據,正常是用UTF-8去解碼,沒有問題,但如果突然API給你返回這樣的數據:

>>> data = b"\xbc cup of flour"
>>> data.decode("utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbc in position 0: invalid start byte

這地方拋出了UnicodeDecodeError錯誤,仔細檢查,發現其實數據的編碼是Latin-1。

>>> data.decode("latin-1")
'¼ cup of flour'

如果你對字符串的編碼不確定,可以使用chardet庫來檢查字符串編碼。

總結

在本文中你已經瞭解了編碼的詳細原理,相信你在以後的編程過程中,再遇到編碼錯誤,相信你能比較從容的解決了。

發佈了109 篇原創文章 · 獲贊 29 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章