前言:在學習編程的過程中,編碼問題搞不透,總有一天會爆炸
Unicode 的出現
ASCII 碼一共規定了 128 個字符的編碼,比如空格 SPACE
是 32(二進制 00100000
),大寫的字母 A
是 65(二進制01000001
)。這 128 個符號(包括 32 個不能打印出來的控制符號),只占用了一個字節的后面 7 位,最前面的一位統一規定為 0
很顯然,128 個字符是遠遠不夠的。急需一個大哥一統江湖,他就是:
Unicode
Unicode 現在的規模可以容納 100 多萬個符號。每個符號的編碼都不一樣,比如,U+0639
表示阿拉伯字母Ain
,U+0041
表示英語的大寫字母 A
,U+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 的出現」的大部分內容摘自阮一峰老師這一篇文章