搞透 python3 中編碼問題

前言:在學習編程的過程中,編碼問題搞不透,總有一天會爆炸

Unicode 的出現

ASCII 碼一共規定了 128 個字符的編碼,比如空格 SPACE 是 32(二進制 00100000),大寫的字母 A 是 65(二進制01000001)。這 128 個符號(包括 32 個不能打印出來的控制符號),只占用了一個字節的后面 7 位,最前面的一位統一規定為 0

很顯然,128 個字符是遠遠不夠的。急需一個大哥一統江湖,他就是:

Unicode

Unicode 現在的規模可以容納 100 多萬個符號。每個符號的編碼都不一樣,比如,U+0639表示阿拉伯字母AinU+0041 表示英語的大寫字母 AU+4E25 表示漢字 。具體的符號對應表,可以查詢unicode.org,或者專門的漢字對應表

Unicode 的不足

Unicode 有很大的問題,假設我們有一個漢字,它的 unicode 編碼是 4E00

那么計算機怎么知道他是漢字的編碼而不是兩個 ASCII 的字符呢?

而且,如果 Unicode 統一規定,每個符號用三個或四個字節表示,那么每個英文字母前都必然有二到三個字節是0,這對于存儲來說是極大的浪費,文本文件的大小會因此大出二三倍,這是無法接受的。

它們造成的結果是:1)出現了 Unicode 的多種存儲方式,也就是說有許多種不同的二進制格式,可以用來表示 Unicode。2)Unicode 在很長一段時間內無法推廣,直到互聯網的出現。

互聯網的普及,強烈要求出現一種統一的編碼方式。UTF-8 就是在互聯網上使用最廣的一種 Unicode 的實現方式。其他實現方式還包括 UTF-16(字符用兩個字節或四個字節表示)和 UTF-32(字符用四個字節表示),不過在互聯網上基本不用。重復一遍,這里的關系是,UTF-8 是 Unicode 的實現方式之一。

UTF8 的出現

上一節我們說:UTF-8的出現解決了上述的問題

UTF-8 可以使用 1~4 個字節表示一個符號,根據不同的符號而變化字節長度

UTF-8 的編碼規則很簡單,只有二條:

  • 對于單字節的符號,字節的第一位設為0,后面 7 位為這個符號的 Unicode 碼。因此對于英語字母,UTF-8 編碼和 ASCII 碼是相同的
  • 對于 n 字節的符號(n > 1),第一個字節的前 n 位都設為 1,第 n + 1 位設為 0,后面字節的前兩位一律設為 10。剩下的沒有提及的二進制位,全部為這個符號的 Unicode 碼

下表總結了編碼規則,字母 x 表示可用編碼的位:

Unicode 符號范圍(十六進制) UTF-8 編碼方式(二進制)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

python3 中的一些機制

首先你要記住的是:

python3 字符串全部用 Unicode 編碼

a = '帥'
b = '\xb8'
c = 'A'
d = '\x12'

沒錯,你用的 IDE 顯示的是 utf-8 編碼。那僅僅是用來文件存儲utf-8

python interpreter 會把字符串(utf8 編碼)用 unicode 的編碼格式寫入內存中,也就是說,內存中的存的是 unicode 的編碼,而我們看見的都是 utf8 編碼

再我們知道:

  • decode()

    解碼 decode('utf-8'),把原來用 utf-8 編碼的二進制解碼為 Unicode 編碼,也就是解碼成 python3 里面的字符串

  • encode()

    編碼 encode('utf-8'),把 Unicode 的字符串編碼為 utf-8 編碼的二進制,也就是我們會看到的 b'xxxx'

來自知乎

讓我們來看一些報錯

a = b'\xb8'
# 默認 utf8
a.decode()

報錯:

由于默認是 utf-8 解碼,也就是說 \xb8 得是 utf-8 編碼的二進制。但顯然不是,因為按照之前說的 utf-8 的編碼方式:b = 1011 前面至少應該要兩個 1。

假如我們有一個字符的 Unicode 編碼是 \xb8,那么我們該如何顯示字符呢?

a = '\xb8'
b = a.encode().decode()
print(b)

輸出:

那為啥這個就不報錯?記住python3 字符串全部用 Unicode 編碼

也就說說:\xb8 是 Unicode 的編碼。你也可以直接用 print 輸出

print('\xb8')

你也可以做一些編碼轉換,中間過程一定是 Unicode

# 帥的 utf8 編碼
a = b'\xe5\xb8\x85'
# 解碼成 unicode 字符串
b = a.decode()
# 由 unicode 字符串再編碼成 gbk
c = b.encode('gbk')

# gbk 編碼
print(c)

參考

文章中 「Unicode 的出現」 「Unicode 的不足」 「UTF8 的出現」的大部分內容摘自阮一峰老師這一篇文章

點個喜歡唄
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容