【Python】中的bytes問(wèn)題

bytes是什么

由上篇文章幾種字符編碼, 我們已經(jīng)知道了ASCII Unicode UTF-8的關(guān)系。而且跃捣,計(jì)算機(jī)只能識(shí)別0和1,那顯然夺蛇,文件存儲(chǔ)在計(jì)算機(jī)中也只能是以二進(jìn)制的形式存儲(chǔ)疚漆,字符編碼在計(jì)算機(jī)中的工作機(jī)制是怎樣的呢?

在計(jì)算機(jī)內(nèi)存中(你打開(kāi)電腦上的一個(gè)文件是要從硬盤(pán)讀取到內(nèi)存中的),統(tǒng)一使用Unicode編碼娶聘。在需要保存到硬盤(pán)或需要傳輸時(shí)灵临,就轉(zhuǎn)化為UTF-8編碼(由上篇文章可知,這樣可以節(jié)省空間趴荸,提高傳輸速度)儒溉。

如,在記事本編輯時(shí)发钝,從文件讀取的UTF-8字符被轉(zhuǎn)化為Unicode字符到內(nèi)存里顿涣,編輯完成,保存時(shí)在將內(nèi)存中的Unicode字符轉(zhuǎn)化為UTF-8保存到文件:

mark

瀏覽網(wǎng)頁(yè)時(shí)酝豪,服務(wù)器會(huì)把動(dòng)態(tài)生成的Unicode字符轉(zhuǎn)化為UTF-8字符再傳輸?shù)綖g覽器:

mark

所以你看到很多網(wǎng)頁(yè)的源碼上會(huì)有類似<meta charset="UTF-8" />的信息涛碑,表示該網(wǎng)頁(yè)正在使用UTF-8編碼。

在python中孵淘,字符串是以Unicode編碼的蒲障,而python的字符串類型是str,內(nèi)存中以Unicode表示瘫证。要在網(wǎng)絡(luò)上進(jìn)行傳輸或保存到磁盤(pán)中揉阎,就需要將str轉(zhuǎn)化為以字節(jié)為單位的bytes

要獲取字符的bytes表示背捌,可以使用encode()方法毙籽,如

>>> 'ABC'.encode('ascii')
b'ABC'
>>>'ABC'.encode('utf-8')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

純英文的str,可以用ascii編碼為bytes毡庆,其內(nèi)容與utf-8相同坑赡。含中文的str不能用ascii編碼為bytes,其超過(guò)了ascii編碼的范圍么抗,會(huì)報(bào)錯(cuò)毅否。

bytes中,無(wú)法顯示為ASCII字符的字節(jié)蝇刀,會(huì)以b\x##的形式顯示螟加。

使用type可以查看b'abc'或b'\xe4\xb8\xad\xe6\x96\x87'的數(shù)據(jù)類型,是一個(gè)bytes類

>>> type(b'\xe4\xb8\xad\xe6\x96\x87')
<class 'bytes'>

相反熊泵,從網(wǎng)絡(luò)上或磁盤(pán)中讀取到了字節(jié)流仰迁,讀到的就是bytes,需要用decode()方法解碼為str

>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

字節(jié)字符串與文本字符串

b'ABC' 與 'ABC'是不同的顽分,前者是bytes,也叫字節(jié)字符串施蜜;后者是str卒蘸,也稱為文本字符串。前者一個(gè)字符占一個(gè)字節(jié)(中文一個(gè)漢字占三個(gè)字節(jié)),str類型在內(nèi)存中以Unicode表示缸沃,一個(gè)字符占若干字節(jié)恰起。

>>> len('下'.encode('utf-8'))
3

在讀取二進(jìn)制數(shù)據(jù)的時(shí)候,字節(jié)字符串和文本字符串可能會(huì)引起錯(cuò)誤趾牧。特別需要注意的是检盼,索引和迭代動(dòng)作返回的是字節(jié)的值而不是字節(jié)字符串。

>>> # Text string
>>> t = 'Hello world'
>>> for x in t:
...     print(x)
...     
H
e
l
l
o
w
o
r
l
d
>>> # Byte string
>>> b = b'Hello world'
>>> for x in b:
...     print(x)
...     
72
101
108
108
111
32
119
111
114
108
100

Base64:顯示與打印二進(jìn)制數(shù)據(jù)

Base64是一種用64個(gè)字符表示任意二進(jìn)制數(shù)據(jù)的方法翘单。

當(dāng)我們用記事本打開(kāi)bmp, exe, jpg文件時(shí)吨枉,會(huì)出現(xiàn)一大堆亂碼:

mark

這是因?yàn)樗鼈儾皇俏谋疚募嵌M(jìn)制文件哄芜,而二進(jìn)制文件包含很多無(wú)法顯示和打印的字符貌亭,所以,如果想讓記事本這樣的文本處理軟件能處理二進(jìn)制數(shù)據(jù)认臊,就需要一個(gè)二進(jìn)制到字符的轉(zhuǎn)換方法圃庭,Base64是一種最常見(jiàn)的二進(jìn)制編碼方法。

注意:通常我們說(shuō)編碼都是將字符編碼成二進(jìn)制失晴,將二進(jìn)制解碼為字符剧腻。而現(xiàn)在我們說(shuō)的是將二進(jìn)制編碼為字符文本,將字符文本解碼為二進(jìn)制涂屁。不要弄混恕酸,筆者在最開(kāi)始學(xué)的時(shí)候全程懵逼,完全搞不明白到底是在解碼還是編碼胯陋。

方法:

準(zhǔn)備一個(gè)包含64個(gè)字符的數(shù)組:

['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']

對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行處理蕊温,每三個(gè)字節(jié)一組,共3 * 8 = 24bit遏乔,劃為4組义矛,每組6個(gè)bit:

mark

我們得到四個(gè)數(shù)字作為索引,查表盟萨,得到對(duì)應(yīng)的4個(gè)字符凉翻,就是編碼后的字符串。

所以捻激,我們是將3個(gè)字節(jié)的二進(jìn)制數(shù)據(jù)編碼為4個(gè)字節(jié)的文本數(shù)據(jù)制轰,長(zhǎng)度增加33%,好處是編碼后的文本可以在郵件正文胞谭、網(wǎng)頁(yè)中正常顯示垃杖。

python內(nèi)置的base64模塊可以提供base64的編解碼功能:

>>> import base64
>>> base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd++//'
>>> base64.b64decode(b'abcd++//')
b'i\xb7\x1d\xfb\xef\xff'

當(dāng)要編碼的二進(jìn)制數(shù)據(jù)不是3的倍數(shù),最后會(huì)剩下1或2個(gè)字符時(shí)怎么辦丈屹?Base64會(huì)自動(dòng)在末尾用b\x00補(bǔ)足后在進(jìn)行解碼调俘,再在編碼的結(jié)尾加上1或2個(gè)=伶棒,以表示在二進(jìn)制數(shù)據(jù)末尾加了幾個(gè)b\x00

但是彩库,在很多Base64編碼中會(huì)把=去掉肤无,因?yàn)樗鼤?huì)在URL,Cookies中造成歧義:

# 標(biāo)準(zhǔn)Base64:
'abcd' -> 'YWJjZA=='
# 自動(dòng)去掉=:
'abcd' -> 'YWJjZA'

去掉=怎么解碼呢,因?yàn)锽ase64編碼后的長(zhǎng)度永遠(yuǎn)是4的整數(shù)倍骇钦,所以將不是4的整數(shù)倍的Base編碼自動(dòng)添加相應(yīng)數(shù)量=后使其變?yōu)?的整數(shù)倍后再解碼即可

你可能會(huì)想宛渐,上面我們已經(jīng)說(shuō)了可以用decode解碼二進(jìn)制數(shù)據(jù),為什么現(xiàn)在還需要對(duì)二進(jìn)制數(shù)據(jù)編碼再顯示呢眯搭?

原因是:上文針對(duì)的是文本文件中的字符窥翩,而像jpg bmp mp3等二進(jìn)制格式文件坦仍,其中的二進(jìn)制數(shù)據(jù)不能正常解析為字符:

>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> b'\xe4\xb8\xad\xe6\x96\x56\x87'.decode('utf-8')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 3-4: invalid continuation byte

可很多時(shí)候我們會(huì)在一些文件頭加一些與文件屬性有關(guān)的數(shù)據(jù)鳍烁,如在jpg文件頭加數(shù)據(jù)表示該圖片的大小、分辨率繁扎、色彩等信息幔荒,這時(shí)我們就需要通過(guò)對(duì)二進(jìn)制進(jìn)行編碼讀取這些信息了。

struct bytes與其他數(shù)據(jù)類型的轉(zhuǎn)換

Bytes之間可以進(jìn)行加法(無(wú)減法操作)組成一個(gè)新的bytes:

>>> m = b'hello '
>>> b = b'world'
>>> m+b
b'hello world'
>>> m+b'world'
b'hello world'

根據(jù)前文已知梳玫,將字符轉(zhuǎn)換成二進(jìn)制可以使用encode()方法爹梁,那如果是非字符型數(shù)據(jù)如整數(shù)、浮點(diǎn)數(shù)怎么轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)呢提澎?

Python提供了一個(gè)struct模塊來(lái)解決bytes與其他數(shù)據(jù)類型之間的轉(zhuǎn)換:

>>> struct.pack('>I', 10240099)
b'\x00\x9c@c'

pack第一個(gè)參數(shù)是處理指令姚垃,'>I'的意思是:

>表示字節(jié)順序是big-endian,也就是網(wǎng)絡(luò)序盼忌,I表示4字節(jié)無(wú)符號(hào)整數(shù)积糯,unsigned int

后面參數(shù)個(gè)數(shù)要與處理指令一致谦纱,大小也要在指定的參數(shù)范圍內(nèi):

>>> struct.pack('>2H', 10245599)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
struct.error: pack expected 2 items for packing (got 1)

>>> struct.pack('>2H', 102456565599)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
struct.error: pack expected 2 items for packing (got 1)

H表示整數(shù)看成,兩字節(jié)無(wú)符號(hào)整數(shù),usigned short跨嘉。

struct模塊定義的數(shù)據(jù)類型可以參考python的官方文檔

mark

相反川慌,unpack指令就用來(lái)將字節(jié)流bytes按給定參數(shù)轉(zhuǎn)化為我們想要的格式:

>>> struct.unpack('>I', b'\x00\x9c@c')
(10240099,)

該句指令的意思是:將給定的字節(jié)流轉(zhuǎn)換成unsigned int類型的4字節(jié)無(wú)符號(hào)整數(shù)。unpack同樣也可以將字節(jié)流轉(zhuǎn)換為字符數(shù)據(jù)祠乃,更換參數(shù)即可梦重。

unpack返回的是tuple類型

應(yīng)用場(chǎng)景

有時(shí)需要用python處理二進(jìn)制數(shù)據(jù),比如存取文件亮瓷,socket操作時(shí)琴拧。這時(shí)可以用python的struct模塊來(lái)完成,比如可以用struct處理c語(yǔ)言中的結(jié)構(gòu)體寺庄。

比如有一個(gè)結(jié)構(gòu)體:

struct Header
{
    unsigned short id;
    char[4] tag;
    unsigned int version;
    unsigned int count;
}

通過(guò)socket.recv接收到了上面的結(jié)構(gòu)體數(shù)據(jù)艾蓝,存在字符串s中力崇,bytes格式斗塘,現(xiàn)在把它解析出來(lái)赢织,可以使用unpack函數(shù):

import struct
id, tag, version, count = struct.unpack('!H4s2I', s)

!表示網(wǎng)絡(luò)字節(jié)順序,因?yàn)閿?shù)據(jù)是從網(wǎng)絡(luò)上接收到的馍盟,再網(wǎng)絡(luò)上傳送時(shí)他是網(wǎng)絡(luò)字節(jié)順序的于置。后面的H4s2I表示1個(gè)unsigned int4s表示4字節(jié)的字符串贞岭,2個(gè)unsigned short八毯。

通過(guò)一個(gè)unpack就將id, tag, version, count數(shù)據(jù)解析好了。

同樣瞄桨,也可以使用pack再將本地?cái)?shù)據(jù)pack成struct格式

ss = struct.pack('>I4s2I', id, tag, version, count)

pack函數(shù)按照指定格式轉(zhuǎn)換成了結(jié)構(gòu)體Header话速,ss現(xiàn)在是一個(gè)字節(jié)流,可以通過(guò)socket將這個(gè)字節(jié)流發(fā)送出去

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芯侥,一起剝皮案震驚了整個(gè)濱河市泊交,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌柱查,老刑警劉巖廓俭,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異唉工,居然都是意外死亡研乒,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)淋硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雹熬,“玉大人,你說(shuō)我怎么就攤上這事谣膳「捅ǎ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵参歹,是天一觀的道長(zhǎng)仰楚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)犬庇,這世上最難降的妖魔是什么僧界? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮臭挽,結(jié)果婚禮上捂襟,老公的妹妹穿的比我還像新娘。我一直安慰自己欢峰,他們只是感情好葬荷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布涨共。 她就那樣靜靜地躺著,像睡著了一般宠漩。 火紅的嫁衣襯著肌膚如雪举反。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天扒吁,我揣著相機(jī)與錄音火鼻,去河邊找鬼。 笑死雕崩,一個(gè)胖子當(dāng)著我的面吹牛魁索,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盼铁,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼粗蔚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了饶火?” 一聲冷哼從身側(cè)響起鹏控,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎趁窃,沒(méi)想到半個(gè)月后牧挣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡醒陆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年瀑构,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刨摩。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寺晌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出澡刹,到底是詐尸還是另有隱情呻征,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布罢浇,位于F島的核電站陆赋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嚷闭。R本人自食惡果不足惜攒岛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胞锰。 院中可真熱鬧灾锯,春花似錦、人聲如沸嗅榕。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至兼雄,卻和暖如春吟逝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背君旦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工澎办, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘲碱,地道東北人金砍。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像麦锯,于是被迫代替她去往敵國(guó)和親恕稠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 前言 最先接觸編程的知識(shí)是在大學(xué)里面,大學(xué)里面學(xué)了一些基礎(chǔ)的知識(shí)料祠,c語(yǔ)言骆捧,java語(yǔ)言,單片機(jī)的匯編語(yǔ)言等髓绽;大學(xué)畢...
    oceanfive閱讀 3,049評(píng)論 0 7
  • 編碼問(wèn)題一直困擾著開(kāi)發(fā)人員敛苇,尤其在 Java 中更加明顯,因?yàn)?Java 是跨平臺(tái)語(yǔ)言顺呕,不同平臺(tái)之間編碼之間的切換...
    x360閱讀 2,470評(píng)論 1 20
  • 字符集和編碼簡(jiǎn)介 在編程中常撤闩剩可以見(jiàn)到各種字符集和編碼,包括ASCII,MBCS,Unicode等字符集株茶。確切的說(shuō)...
    蘭山小亭閱讀 8,466評(píng)論 0 13
  • 說(shuō)明:本文是我在readthedocs看到的来涨,覺(jué)得很不錯(cuò)所以轉(zhuǎn)載過(guò)來(lái),有刪改启盛,原文地址點(diǎn)這里蹦掐。 實(shí)用Unicode...
    aurora閱讀 977評(píng)論 0 6
  • 我是一個(gè)曾經(jīng)被嚴(yán)重失眠困擾了一年零三個(gè)月的人。 嚴(yán)重到什么程度呢僵闯? 很困卧抗,很困很困,眼睛完全睜不開(kāi)棍厂,腦子混沌不清颗味,...
    奧尼恩小姐閱讀 815評(píng)論 0 49