摘要:只包含了個(gè)基本拉丁字母阿拉伯?dāng)?shù)目字和英式標(biāo)點(diǎn)符號(hào)一共個(gè)字符,因此只需要不占滿一個(gè)字節(jié)就可以存儲(chǔ),而則涵蓋的數(shù)據(jù)除了視覺(jué)上的字形編碼方法標(biāo)準(zhǔn)的字符編碼外,還包含了字符特性,如大小寫字母,共可包含個(gè)字符,而到現(xiàn)在只填充了其中的個(gè)位置。
項(xiàng)目地址:https://git.io/pytips
0x07 和 0x08 分別介紹了 Python 中的字符串類型(str)和字節(jié)類型(byte),以及 Python 編碼中最常見(jiàn)也是最頑固的兩個(gè)錯(cuò)誤:
UnicodeEncodeError: "ascii" codec can"t encode characters in position 0-1: ordinal not in range(128)
UnicodeDecodeError: "utf-8" codec can"t decode bytes in position 0-1: invalid continuation byte
這一期就從這兩個(gè)錯(cuò)誤入手,分析 Python 中 Unicode 的正確用法。這篇短文并不能保證你可以永遠(yuǎn)杜絕上面兩個(gè)錯(cuò)誤,但是希望在下次遇到這個(gè)錯(cuò)誤的時(shí)候知道錯(cuò)在哪里、應(yīng)該從哪里入手。
編碼與解碼上面的兩個(gè)錯(cuò)誤分別是 UnicodeEncodeError 和 UnicodeDecodeError,也就是說(shuō)分別在 Unicode 編碼(Encode)和解碼(Decode)過(guò)程中出現(xiàn)了錯(cuò)誤,那么編碼和解碼究竟分別意味著什么?根據(jù)維基百科字符編碼的定義:
字符編碼(英語(yǔ):Character encoding)、字集碼是把字符集中的字符編碼為指定集合中某一對(duì)象(例如:比特模式、自然數(shù)序列、8位組或者電脈沖),以便文本在計(jì)算機(jī)中存儲(chǔ)和通過(guò)通信網(wǎng)絡(luò)的傳遞。
簡(jiǎn)單來(lái)說(shuō)就是把人類通用的語(yǔ)言符號(hào)翻譯成計(jì)算機(jī)通用的對(duì)象,而反向的翻譯過(guò)程自然就是解碼了。Python 中的字符串類型代表人類通用的語(yǔ)言符號(hào),因此字符串類型有encode()方法;而字節(jié)類型代表計(jì)算機(jī)通用的對(duì)象(二進(jìn)制數(shù)據(jù)),因此字節(jié)類型有decode()方法。
print("??".encode())
b"xf0x9fx8cx8exf0x9fx8cx8f"
print(b"xf0x9fx8cx8exf0x9fx8cx8f".decode())
??
既然說(shuō)編碼和解碼都是翻譯的過(guò)程,那么就需要一本字典將人類和計(jì)算機(jī)的語(yǔ)言一一對(duì)應(yīng)起來(lái),這本字典的名字叫做字符集,從最早的 ASCII 到現(xiàn)在最通用的 Unicode,它們的本質(zhì)是一樣的,只是兩本字典的厚度不同而已。ASCII 只包含了26個(gè)基本拉丁字母、阿拉伯?dāng)?shù)目字和英式標(biāo)點(diǎn)符號(hào)一共128個(gè)字符,因此只需要(不占滿)一個(gè)字節(jié)就可以存儲(chǔ),而 Unicode 則涵蓋的數(shù)據(jù)除了視覺(jué)上的字形、編碼方法、標(biāo)準(zhǔn)的字符編碼外,還包含了字符特性,如大小寫字母,共可包含 1.1M 個(gè)字符,而到現(xiàn)在只填充了其中的 110K 個(gè)位置。
字符集中字符所存儲(chǔ)的位置(或者說(shuō)對(duì)應(yīng)的計(jì)算機(jī)通用的數(shù)字)稱之為碼位(code point),例如在 ASCII 中字符 "$" 的碼位就是:
print(ord("$"))
36
ASCII 只需要一個(gè)字節(jié)就能存下所有碼位,而 Unicode 則需要幾個(gè)字節(jié)才能容納,但是對(duì)于具體采用什么樣的方案來(lái)實(shí)現(xiàn) Unicode 的這種映射關(guān)系,也有很多不同的方案(或規(guī)則),例如最常見(jiàn)(也是 Python 中默認(rèn)的)UTF-8,還有 UTF-16、UTF-32 等,對(duì)于它們規(guī)則上的不同這里就不深入展開(kāi)了。當(dāng)然,在 ASCII 與 Unicode 之間還有很多其他的字符集與編碼方案,例如中文編碼的 GB2312、繁體字的 Big5 等等,這并不影響我們對(duì)編碼與解碼過(guò)程的理解。
Unicode*Error明白了字符串與字節(jié),編碼與解碼之后,讓我們手動(dòng)制造上面兩個(gè) Unicode*Error 試試,首先是編碼錯(cuò)誤:
def tryEncode(s, encoding="utf-8"): try: print(s.encode(encoding)) except UnicodeEncodeError as err: print(err) s = "$" # UTF-8 String tryEncode(s) # 默認(rèn)用 UTF-8 進(jìn)行編碼 tryEncode(s, "ascii") # 嘗試用 ASCII 進(jìn)行編碼 s = "雨" # UTF-8 String tryEncode(s) # 默認(rèn)用 UTF-8 進(jìn)行編碼 tryEncode(s, "ascii") # 嘗試用 ASCII 進(jìn)行編碼 tryEncode(s, "GB2312") # 嘗試用 GB2312 進(jìn)行編碼
b"$" b"$" b"xe9x9bxa8" "ascii" codec can"t encode character "u96e8" in position 0: ordinal not in range(128) b"xd3xea"
由于 UTF-8 對(duì) ASCII 的兼容性,"$" 可以用 ASCII 進(jìn)行編碼;而 "雨" 則無(wú)法用 ASCII 進(jìn)行編碼,因?yàn)樗呀?jīng)超出了 ASCII 字符集的 128 個(gè)字符,所以引發(fā)了 UnicodeEncodeError;而 "雨" 在 GB2312 中的碼位是 b"xd3xea",與 UTF-8 不同,但是仍然可以正確編碼。因此如果出現(xiàn)了 UnicodeEncodeError 說(shuō)明你用錯(cuò)了字典,要翻譯的字符沒(méi)辦法正確翻譯成碼位!
再來(lái)看解碼錯(cuò)誤:
def tryDecode(s, decoding="utf-8"): try: print(s.decode(decoding)) except UnicodeDecodeError as err: print(err) b = b"$" # Bytes tryDecode(b) # 默認(rèn)用 UTF-8 進(jìn)行解碼 tryDecode(b, "ascii") # 嘗試用 ASCII 進(jìn)行解碼 tryDecode(b, "GB2312") # 嘗試用 GB2312 進(jìn)行解碼 b = b"xd3xea" # 上面例子中通過(guò) GB2312 編碼得到的 Bytes tryDecode(b) # 默認(rèn)用 UTF-8 進(jìn)行解碼 tryDecode(b, "ascii") # 嘗試用 ASCII 進(jìn)行解碼 tryDecode(b, "GB2312") # 嘗試用 GB2312 進(jìn)行解碼 tryDecode(b, "GBK") # 嘗試用 GBK 進(jìn)行解碼 tryDecode(b, "Big5") # 嘗試用 Big5 進(jìn)行解碼 tryDecode(b.decode("GB2312").encode()) # Byte-Decode-Unicode-Encode-Byte
$ $ $ "utf-8" codec can"t decode byte 0xd3 in position 0: invalid continuation byte "ascii" codec can"t decode byte 0xd3 in position 0: ordinal not in range(128) 雨 雨 迾 雨
一般后續(xù)出現(xiàn)的字符集都是對(duì) ASCII 兼容的,可以認(rèn)為 ASCII 是他們的一個(gè)子集,因此可以用 ASCII 進(jìn)行解碼(編碼)的,一般也可以用其它方法;對(duì)于不是不存在子集關(guān)系的編碼,強(qiáng)行解碼有可能會(huì)導(dǎo)致錯(cuò)誤或亂碼!
實(shí)踐中的策略清楚了上面介紹的所有原理之后,在時(shí)間操作中應(yīng)該怎樣規(guī)避錯(cuò)誤或亂碼呢?
記清楚編碼與解碼的方向;
在 Python 中的操作盡量采用 UTF-8,輸入或輸出的時(shí)候再根據(jù)需求確定是否需要編碼成二進(jìn)制:
# cat utf8.txt # 你好,世界! # file utf8.txt # utf8.txt: UTF-8 Unicode text with open("utf8.txt", "rb") as f: content = f.read() print(content) print(content.decode()) with open("utf8.txt", "r") as f: print(f.read()) # cat gb2312.txt # 你好,Unicode! # file gb2312.txt # gb2312.txt: ISO-8859 text with open("gb2312.txt", "r") as f: try: print(f.read()) except: print("Failed to decode file!") with open("gb2312.txt", "rb") as f: print(f.read().decode("gb2312"))
b"xe4xbdxa0xe5xa5xbdxefxbcx8cxe4xb8x96xe7x95x8cxefxbcx81 " 你好,世界! 你好,世界! Failed to decode file! 你好,Unicode!
歡迎關(guān)注 PyHub!
Pragmatic Unicode
字符編碼筆記:ASCII,Unicode和UTF-8
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/37832.html
摘要:回到對(duì)字節(jié)和字節(jié)數(shù)組的定義為了用計(jì)算機(jī)可以理解的數(shù)字描述人類使用的字符,我們需要一張數(shù)字與字符對(duì)應(yīng)的表。由于和字符串一樣是序列類型,字節(jié)和字節(jié)數(shù)組可用的方法也類似,這里就不一一列舉了。 項(xiàng)目地址:https://git.io/pytips 0x07 中介紹了 Python 中的字符串類型,字符串類型是對(duì)人類友好的符號(hào),但計(jì)算機(jī)只認(rèn)識(shí)一種符號(hào),那就是二進(jìn)制(binary)數(shù),或者說(shuō)是數(shù)字...
摘要:項(xiàng)目地址所有用過(guò)的人應(yīng)該都看過(guò)下面兩行錯(cuò)誤信息這就是界的錕斤拷今天和接下來(lái)幾期的內(nèi)容將主要關(guān)注中的字符串字節(jié)及兩者之間的相互轉(zhuǎn)換。 項(xiàng)目地址:https://git.io/pytips 所有用過(guò) Python (2&3)的人應(yīng)該都看過(guò)下面兩行錯(cuò)誤信息: UnicodeEncodeError: ascii codec cant encode characters in position...
摘要:模塊的導(dǎo)入一定要放在最上方,也就是在所有其它模塊之前導(dǎo)入。最后一列是每個(gè)新特性所對(duì)應(yīng)的及簡(jiǎn)單描述。相對(duì)導(dǎo)入則可以使用為標(biāo)記導(dǎo)入相對(duì)目錄中的模塊,具體可以參考這篇文章導(dǎo)入模塊的幾種姿勢(shì)。 項(xiàng)目地址:https://git.io/pytips 我們經(jīng)常從一些組織良好的 Python 項(xiàng)目中看到 __future__ 的身影,例如: from __future__ import absolu...
摘要:可以通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)展示當(dāng)然,也可以用狀態(tài)變量的做法來(lái)替代總結(jié)有人覺(jué)得的這些用法違反直覺(jué)或者是而非,不值得提倡。 項(xiàng)目地址:https://git.io/pytips 我們都知道 Python 中 else 的基本用法是在條件控制語(yǔ)句中的 if...elif...else...,但是 else 還有兩個(gè)其它的用途,一是用于循環(huán)的結(jié)尾,另一個(gè)是用在錯(cuò)誤處理的 try 中。這原本是 P...
摘要:中的枚舉類型枚舉類型可以看作是一種標(biāo)簽或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期月份狀態(tài)等。 Python 中的枚舉類型 枚舉類型可以看作是一種標(biāo)簽或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、狀態(tài)等。Python 的原生類型(Built-in types)里并沒(méi)有專門的枚舉類型,但是我們可以通過(guò)很多方法來(lái)實(shí)現(xiàn)它,例如字典、類等: WEEKD...
閱讀 1385·2021-10-19 11:42
閱讀 726·2021-09-22 16:04
閱讀 1878·2021-09-10 11:23
閱讀 1855·2021-07-29 14:48
閱讀 1256·2021-07-26 23:38
閱讀 2819·2019-08-30 15:54
閱讀 1032·2019-08-30 11:25
閱讀 1702·2019-08-29 17:23