2012-04-04 122 views
9

我知道這看起來令人尷尬的容易,我想這個問題是我根本沒有這一切字節-STR-unicode的清醒的認識(和編碼解碼,坦率地說)的東西呢。解碼的base64字符串在Python 3(與LXML與否)

我一直試圖讓我的工作代碼在Python 3上運行。我堅持使用的部分是當我用lxml解析XML並解碼該XML中的base64字符串時。

代碼現在工作以下列方式:

我用XPath查詢'.../binary/text()'檢索二進制數據。這會生成一個包含lxml.etree._ElementUnicodeResult對象的單元素列表。然後,與Python 2,我是能夠做到:

decoded = source.decode('base64') 

最後

output = numpy.frombuffer(decoded) 

然而,在Python 3中,我得到一個錯誤信息說

AttributeError: 'lxml.etree._ElementUnicodeResult' object has no attribute 'decode' 

事實並非如此令人驚訝,因爲lxml.etree._ElementUnicodeResultstr的子類。

另一種方法是,以獲得真正的str與它相同的數據與

binary = tree.xpath('//binary')[0] 
binary_string = binary.text 

這將是基本相同的。那麼我該怎麼做從base64解碼它呢?我已經看了base64模塊,但它需要一個bytes對象作爲參數,我想不出將str作爲bytes的方式,因爲如果我嘗試構造一個bytes對象,Python將嘗試編碼字符串,我不需要。

進一步谷歌搜索,我整個binascii模塊來(這是從base64間接調用,無論如何,如果我沒有記錯的話),但我的字符串調用binascii.b2a_base64()產生

TypeError: 'str' does not support the buffer interface 

附:我甚至在how to decode a hex string in Python 3上找到了一個回答問題,但是這是通過專門的方法bytes.fromhex()完成的,所以我不認爲它會有幫助。

可能有人請告訴我,我缺少的是什麼?恐怕大部分帖子都是無關緊要的,只會加重我的恥辱,但至少你們知道what I tried

+4

順便說一句,Ned Batchelder在這個bytes-str-unicode的東西上有一個很棒的介紹:[Pragmatic Unicode,或者:如何阻止疼痛?](http:/ /nedbatchelder.com/text/unipain.html) – delnan 2012-04-04 21:11:08

+0

謝謝@delnan,我到一半,真正幫助了很多已經:) – 2012-04-04 21:51:10

回答

2

我沒有Python 3的安裝,但它聽起來像你需要轉換Unicode的從LXML返回字節,也許是通過調用.encode(「ASCII」)?

+0

天哪......我知道這是很容易。我無法按照應有的方式來解決這個問題。我一直在想我的字符串是* *編碼的*,所以我不會想到我需要對它進行編碼以獲得'字節'。謝謝。 – 2012-04-04 21:24:25

+3

思考的Unicode爲需要進行編碼時,他們將「硬件」和解碼從:-) – thebjorn 2012-04-04 21:28:19

+0

我覺得像這麼長的問題,需要一個較長的答案「硬件」何時到來純香草字符串,但不管怎麼說,非常感謝您指出正確的方向:) – 2012-04-05 12:09:09

6

OK,我想我會總結我目前的東西(隨時糾正我)認識。希望它能幫助那些像我一樣困惑的人。

信用完全去thebjorndelnan,當然。

因此,從最常見的東西開始: 有Unicode,它是一個全局標準,它將代碼(或代碼點)分配給所有您可以想象的奇特字符。這些代碼只是整數。從Unicode 6.1開始,有109,975個圖形字符,維基百科說。

然後有編碼定義如何用字節碼指定Unicode字符。一個字節不足以指定任意的Unicode字符。儘管如果你只需要一小部分(英文字母,數字,標點符號和一些控制字符),你可以使用每個字符一個字節(甚至7位;參見ASCII)。


要在任何地方傳遞一個Unicode字符串,需要以字節爲單位進行編碼,然後才能在另一端進行解碼。

在Python 2中,str實際上是字節,而unicode是Unicode,但是Python 2會在需要時爲你做隱式編碼/解碼。它會嘗試使用ASCII編碼。

在Python 3中,str始終是一個Unicode字符串,而bytes是實際字節的新數據類型。 Python 3不會執行隱式轉換,您始終需要自己完成並指定編碼。這意味着,只有在你明白髮生了什麼事情之後,你的程序纔會起作用,這完全發生在我身上。


現在,或多或少清晰,讓我們繼續base64編碼,這也是各種各樣的編碼,但有一個稍微不同的含義。 假設你有一些二進制數據(即字節),這可能意味着什麼(在我的情況下,它是一堆float s)。現在你想用一個字符串表示這個二進制數組。這就是base64編碼的含義:您將您的字節表示爲ASCII字符串。

Base64表示6位,所以在base64編碼的字符串中,單個字符代表6位數據。這就是爲什麼base64編碼的字符串需要長度爲4的倍數:否則編碼的字節數將不是整數。


最後,要從base64解碼,您需要一個ASCII字符串。一個Unicode字符串不行,只能有base64字母表中的字符。 Base64 module在Python中完成這項工作。 base64.b64decode()函數採用字節字符串作爲參數。在Python 2中,它表示:str。在Python 3中,它表示:bytes。所以,如果你有一個str,如

>>> s = 'U3RhY2sgT3ZlcmZsb3c=' 

在Python 2,你可以只是做

>>> s.decode('base64') 

因爲s已經在ASCII。 在Python 3,您需要編碼它在ASCII第一,所以你要做的:

>>> base64.b64decode(s.encode('ascii')) 

順便說一下,這將返回一個bytes對象,所以這真的取決於你如何然後處理那些字節。也許這是我的花車,但也許你應該嘗試把它當作ASCII解碼:) 在Python 2但它只是一個str。無論如何,看看struct的工具來解壓你的數據從這些字節。

所以,如果你需要的代碼到兩個Python 2和3的工作,去的最後一個。爲了確保您使用Unicode到底(如果你是從解碼的base64文本),你必須對它進行解碼:

>>> base64.b64decode(s.encode('ascii')).decode('ascii') 

在Python 2中,因爲它適用於strencode('ascii')將不能有效地做任何事。所以它會先執行一個隱式轉換到Unicode,然後做你想做的事(把它轉換回ASCII)。 decode('ascii')將在Python 2上返回一個unicode對象。

+0

優秀的總結:-)如果你想保存一個浮點列表,也許pickle模塊比結構模塊更容易?喜歡的東西base64.b64encode(pickle.dumps([2.718,3.141])) – thebjorn 2012-04-05 18:07:56

+0

@thebjorn謝謝:)我實際使用'numpy.frombuffer()',我剛纔提到的'參考struct',佔一般案件。 – 2012-04-05 18:24:36

+0

「只有在你明白髮生了什麼之後,你的程序纔會起作用」 - 大多數情況下,這是一件好事。 :) – AKX 2012-04-06 23:01:04