偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Python編碼為什么那么蛋疼?

開(kāi)發(fā) 后端
據(jù)說(shuō),每個(gè)做 Python 開(kāi)發(fā)的都被字符編碼的問(wèn)題搞暈過(guò),最常見(jiàn)的錯(cuò)誤就是 UnicodeEncodeError、UnicodeDecodeError,你好像知道怎么解決,遺憾的是,錯(cuò)誤又出現(xiàn)在其它地方,問(wèn)題總是重蹈覆轍,str 到 unicode 之間的轉(zhuǎn)換用 decode 還是 encode 方法還特不好記,老是混淆,問(wèn)題究竟出在哪里?

據(jù)說(shuō),每個(gè)做 Python 開(kāi)發(fā)的都被字符編碼的問(wèn)題搞暈過(guò),最常見(jiàn)的錯(cuò)誤就是 UnicodeEncodeError、UnicodeDecodeError,你好像知道怎么解決,遺憾的是,錯(cuò)誤又出現(xiàn)在其它地方,問(wèn)題總是重蹈覆轍,str 到 unicode 之間的轉(zhuǎn)換用 decode 還是 encode 方法還特不好記,老是混淆,問(wèn)題究竟出在哪里?

為了弄清楚這個(gè)問(wèn)題,我決定從 python 字符串的構(gòu)成以及字符編碼的細(xì)節(jié)上進(jìn)行深入淺出的分析

字節(jié)與字符

計(jì)算機(jī)存儲(chǔ)的一切數(shù)據(jù),文本字符、圖片、視頻、音頻、軟件都是由一串01的字節(jié)序列構(gòu)成的,一個(gè)字節(jié)等于8個(gè)比特位。

而字符就是一個(gè)符號(hào),比如一個(gè)漢字、一個(gè)英文字母、一個(gè)數(shù)字、一個(gè)標(biāo)點(diǎn)都可以稱為一個(gè)字符。

字節(jié)方便存儲(chǔ)和網(wǎng)絡(luò)傳輸,而字符用于顯示,方便閱讀。例如字符 “p” 存儲(chǔ)到硬盤(pán)是一串二進(jìn)制數(shù)據(jù) 01110000,占用一個(gè)字節(jié)的長(zhǎng)度

編碼與解碼

我們用編輯器打開(kāi)的文本,看到的一個(gè)個(gè)字符,最終保存在磁盤(pán)的時(shí)候都是以二進(jìn)制字節(jié)序列形式存起來(lái)的。那么從字符到字節(jié)的轉(zhuǎn)換過(guò)程就叫做編碼(encode),反過(guò)來(lái)叫做解碼(decode),兩者是一個(gè)可逆的過(guò)程。編碼是為了存儲(chǔ)傳輸,解碼是為了方便顯示閱讀。

例如字符 “p” 經(jīng)過(guò)編碼處理保存到硬盤(pán)是一串二進(jìn)制字節(jié)序列 01110000 ,占用一個(gè)字節(jié)的長(zhǎng)度。字符 “禪” 有可能是以 “11100111 10100110 10000101″ 占用3個(gè)字節(jié)的長(zhǎng)度存儲(chǔ),為什么說(shuō)是有可能呢?這個(gè)放到后面再說(shuō)。

Python 的編碼為什么那么蛋疼?當(dāng)然,這不能怪開(kāi)發(fā)者。

這是因?yàn)?Python2 使用 ASCII 字符編碼作為默認(rèn)編碼方式,而 ASCII 不能處理中文,那么為什么不用 UTf-8 呢?因?yàn)? Guido 老爹為 Python 編寫(xiě)第一行代碼是在1989年的冬天,1991年2月正式開(kāi)源發(fā)布了第一個(gè)版本,而 Unicode 是1991年10月發(fā)布的,也就是說(shuō) Python 這門(mén)語(yǔ)言創(chuàng)立的時(shí)候 UTF-8 還沒(méi)誕生,這是其一。

Python 把字符串的類型還搞成兩種,unicode 和 str ,以至于把開(kāi)發(fā)者都弄糊涂了,這是其二。python3 徹底把 字符串重新改造了,只保留一種類型,這是后話,以后再說(shuō)。

str與unicode

Python2 把字符串分為 unicode 和 str 兩種類型。本質(zhì)上 str 是一串二進(jìn)制字節(jié)序列,下面的示例代碼可以看出 str 類型的 “禪” 打印出來(lái)是十六進(jìn)制的 \xec\xf8 ,對(duì)應(yīng)的二進(jìn)制字節(jié)序列就是 ’11101100 11111000′。

>>> s = '禪'
>>> s
'\xec\xf8'
>>> type(s)
<type 'str'>

而 unicode 類型的 u”禪” 對(duì)應(yīng)的 unicode 符號(hào)是 u’\u7985′

>>> u = u"禪"
>>> u
u'\u7985'
>>> type(u)
<type 'unicode'>

我們要把 unicode 符號(hào)保存到文件或者傳輸?shù)骄W(wǎng)絡(luò)就需要經(jīng)過(guò)編碼處理轉(zhuǎn)換成 str 類型,于是 python 提供了 encode 方法,從 unicode 轉(zhuǎn)換到 str,反之亦然。

encode

>>> u = u"禪"
>>> u
u'\u7985'
>>> u.encode("utf-8")
'\xe7\xa6\x85'

decode

>>> s = "禪"
>>> s.decode("utf-8")
u'\u7985'
>>>

不少初學(xué)者怎么也記不住 str 與 unicode 之間的轉(zhuǎn)換用 encode 還是 decode,如果你記住了 str 本質(zhì)上其實(shí)是一串二進(jìn)制數(shù)據(jù),而 unicode 是字符(符號(hào)),編碼(encode)就是把字符(符號(hào))轉(zhuǎn)換為 二進(jìn)制數(shù)據(jù)的過(guò)程,因此 unicode 到 str 的轉(zhuǎn)換要用 encode 方法,反過(guò)來(lái)就是用 decode 方法。

encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string”.

清楚了 str 與 unicode 之間的轉(zhuǎn)換關(guān)系之后,我們來(lái)看看什么時(shí)候會(huì)出現(xiàn) UnicodeEncodeError、UnicodeDecodeError 錯(cuò)誤。

UnicodeEncodeError

UnicodeEncodeError 發(fā)生在 unicode 字符串轉(zhuǎn)換成 str 字節(jié)序列的時(shí)候,來(lái)看一個(gè)例子,把一串 unicode 字符串保存到文件

# -*- coding:utf-8 -*-
def main():
    name = u'Python之禪'
    f = open("output.txt", "w")
    f.write(name)

錯(cuò)誤日志

UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 6-7: ordinal not in range(128)

為什么會(huì)出現(xiàn) UnicodeEncodeError?

因?yàn)檎{(diào)用 write 方法時(shí),Python 會(huì)先判斷字符串是什么類型,如果是 str,就直接寫(xiě)入文件,不需要編碼,因?yàn)?str 類型的字符串本身就是一串二進(jìn)制的字節(jié)序列了。

如果字符串是 unicode 類型,那么它會(huì)先調(diào)用 encode 方法把 unicode 字符串轉(zhuǎn)換成二進(jìn)制形式的 str 類型,才保存到文件,而 encode 方法會(huì)使用 python 默認(rèn)的 ascii 碼來(lái)編碼

相當(dāng)于:

>>> u"Python之禪".encode("ascii")

但是,我們知道 ASCII 字符集中只包含了128個(gè)拉丁字母,不包括中文字符,因此 出現(xiàn)了 ‘ascii’ codec can’t encode characters 的錯(cuò)誤。要正確地使用 encode ,就必須指定一個(gè)包含了中文字符的字符集,比如:UTF-8、GBK。

>>> u"Python之禪".encode("utf-8")
'Python\xe4\xb9\x8b\xe7\xa6\x85'

>>> u"Python之禪".encode("gbk")
'Python\xd6\xae\xec\xf8'

所以要把 unicode 字符串正確地寫(xiě)入文件,就應(yīng)該預(yù)先把字符串進(jìn)行 UTF-8 或 GBK 編碼轉(zhuǎn)換。

def main():
    name = u'Python之禪'
    name = name.encode('utf-8')
    with open("output.txt", "w") as f:
        f.write(name)

當(dāng)然,把 unicode 字符串正確地寫(xiě)入文件不止一種方式,但原理是一樣的,這里不再介紹,把字符串寫(xiě)入數(shù)據(jù)庫(kù),傳輸?shù)骄W(wǎng)絡(luò)都是同樣的原理

UnicodeDecodeError

UnicodeDecodeError 發(fā)生在 str 類型的字節(jié)序列解碼成 unicode 類型的字符串時(shí)

>>> a = u"禪"
>>> a
u'\u7985'
>>> b = a.encode("utf-8")
>>> b
'\xe7\xa6\x85'
>>> b.decode("gbk")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'gbk' codec can't decode byte 0x85 in position 2: incomplete multibyte sequence

把一個(gè)經(jīng)過(guò) UTF-8 編碼后生成的字節(jié)序列 ‘\xe7\xa6\x85′ 再用 GBK 解碼轉(zhuǎn)換成 unicode 字符串時(shí),出現(xiàn) UnicodeDecodeError,因?yàn)?(對(duì)于中文字符)GBK 編碼只占用兩個(gè)字節(jié),而 UTF-8 占用3個(gè)字節(jié),用 GBK 轉(zhuǎn)換時(shí),還多出一個(gè)字節(jié),因此它沒(méi)法解析。避免 UnicodeDecodeError 的關(guān)鍵是保持 編碼和解碼時(shí)用的編碼類型一致。

這也回答了文章開(kāi)頭說(shuō)的字符 “禪”,保存到文件中有可能占3個(gè)字節(jié),有可能占2個(gè)字節(jié),具體處決于 encode 的時(shí)候指定的編碼格式是什么。

再舉一個(gè) UnicodeDecodeError 的例子

>>> x = u"Python"
>>> y = "之禪"
>>> x + y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128) >>>

str 與 unicode 字符串 執(zhí)行 + 操作是,Python 會(huì)把 str 類型的字節(jié)序列隱式地轉(zhuǎn)換成(解碼)成 和 x 一樣的 unicode 類型,但Python是使用默認(rèn)的 ascii 編碼來(lái)轉(zhuǎn)換的,而 ASCII 中不包含中文,所以報(bào)錯(cuò)了。

>>> y.decode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

正確地方式應(yīng)該是顯示地把 y 用 UTF-8 或者 GBK 進(jìn)行解碼。

>>> x = u"Python"
>>> y = "之禪"
>>> y = y.decode("utf-8")
>>> x + y
u'Python\u4e4b\u7985'

以上內(nèi)容都是基于 Python2 來(lái)講的,關(guān)于 Python3 的字符和編碼將會(huì)另開(kāi)一篇文章來(lái)寫(xiě),保持關(guān)注。

責(zé)任編輯:張燕妮 來(lái)源: liuzhijun
相關(guān)推薦

2023-06-08 18:25:40

Doris場(chǎng)景查詢

2021-06-09 09:32:58

Esbuild 工具前端

2020-08-03 07:50:56

存儲(chǔ)對(duì)象存儲(chǔ)

2020-10-12 07:30:31

Chrome下載

2020-04-24 08:15:51

代碼 if else數(shù)組

2025-05-27 02:20:00

PG數(shù)據(jù)庫(kù)DBA

2023-11-20 17:38:07

Djangoagtailadmin

2015-06-05 14:15:13

程序員難升職

2022-02-21 10:06:14

自動(dòng)駕駛汽車智能

2017-01-21 14:57:43

Linuxsystemd

2025-08-01 02:11:00

2013-07-04 14:05:26

功能用戶體驗(yàn)

2023-11-07 15:11:46

Kafka技巧

2025-08-05 07:58:28

2020-01-22 16:36:52

MYSQL開(kāi)源數(shù)據(jù)庫(kù)

2023-10-15 12:23:10

單線程Redis

2024-07-17 09:39:05

軟件版本號(hào)管理

2022-08-17 14:35:27

智慧城市物聯(lián)網(wǎng)社區(qū)

2019-12-02 14:22:01

浪費(fèi)云計(jì)算支出

2018-11-08 14:39:50

Excel表結(jié)構(gòu)數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)