12

我知道这看起来非常容易,我想问题是我还没有清楚地了解所有这些bytes-str-unicode(和encoding-decoding,坦率地说)的东西。

我一直在尝试让我的工作代码在 Python 3 上运行。我坚持的部分是当我解析 XMLlxml并解码该 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._ElementUnicodeResult它是str.

str另一种方法是用相同的数据获得一个真实的

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

那基本上是一样的。那么我该怎么做才能从base64解码呢?我看过这个base64模块,但它需要一个bytes对象作为参数,我想不出如何呈现strbytes,因为如果我尝试构造一个bytes对象,Python 会尝试对字符串进行编码,而我不这样做不需要。

进一步谷歌搜索,我发现了这个模块(如果我没记错的话,binascii它是间接调用的),但是调用我的字符串会产生base64binascii.b2a_base64()

TypeError: 'str' does not support the buffer interface

PS我什至找到了一个关于如何在 Python 3 中解码十六进制字符串的已回答问题,但这是通过专用方法完成的,bytes.fromhex()所以我看不出它会有什么帮助。

有人可以告诉我我错过了什么吗?恐怕大部分帖子都无关紧要,只会加剧我的耻辱,但至少你们知道我尝试了什么

4

2 回答 2

11

好的,我想我将总结一下我目前对事物的理解(请随时纠正我)。希望它能帮助像我一样困惑的其他人。

当然,这完全归功于 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编码,它也是一种编码,但含义略有不同。假设您有一些二进制数据(即bytes)可能意味着任何东西(在我的情况下它是一堆floats)。现在你想用一个字符串来表示这个二进制数组。这就是 base64 编码的含义:您将字节表示为 ASCII 字符串。

Base64 表示 6 位,因此在 base64 编码的字符串中,单个字符代表 6 位数据。这就是为什么 base64 编码的字符串长度必须是 4 的倍数:否则编码的字节数将不是整数。


最后,要从 base64 解码,您需要一个 ASCII 字符串。Unicode 字符串不行,只能有 base64 字母表中的字符。Base64 模块在 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 上,encode('ascii')不会有效地做任何事情,因为它应用于str. 所以它会先隐式转换为 Unicode,然后做你想做的事情(将其转换回 ASCII)。将在 Python 2 上decode('ascii')返回一个对象。unicode

于 2012-04-05T12:07:57.060 回答
2

我没有安装 Python 3,但听起来你需要将从 lxml 返回的 Unicode 转换为字节,也许通过调用 .encode('ascii') ?

于 2012-04-04T21:16:53.460 回答