为什么我需要'b'用Base64编码Pythonstring?

在这个python示例之后 ,我使用下面的代码将一个string编码为Base64:

>>> import base64 >>> encoded = base64.b64encode(b'data to be encoded') >>> encoded b'ZGF0YSB0byBiZSBlbmNvZGVk' 

但是,如果我离开领导b

 >>> encoded = base64.b64encode('data to be encoded') 

我得到以下错误:

 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Python32\lib\base64.py", line 56, in b64encode raise TypeError("expected bytes, not %s" % s.__class__.__name__) TypeError: expected bytes, not str 

为什么是这样?

base64编码采用8位二进制字节数据,并对其进行编码,仅使用AZaz0-9+/ *字符,因此可以通过不保留所有8位数据的通道(如电子邮件)进行传输。

因此,它需要一个8位字节的string。 你用Python b''语法创build了Python 3。

如果你删除了b ,它就变成了一个string。 一个string是一个Unicode字符序列。 base64不知道如何处理Unicode数据,它不是8位的。 事实上,这实际上并不是真的。 🙂

在你的第二个例子中:

 >>> encoded = base64.b64encode('data to be encoded') 

所有的字符都适合ASCII字符集,因此base64编码实际上是没有意义的。 你可以把它转换成ascii,而不是

 >>> encoded = 'data to be encoded'.encode('ascii') 

或者更简单:

 >>> encoded = b'data to be encoded' 

在这种情况下,这将是同样的事情。


*大多数base64风格也可能包括a =在最后作为填充。 另外,一些base64变体可能会使用+/以外的字符。 查看维基百科的变体汇总表以获得总览。

简答

您需要将类似bytes-like对象( bytesbytearray等) base64.b64encode()方法。 这里有两种方法:

 >>> data = base64.b64encode(b'data to be encoded') >>> print(data) b'ZGF0YSB0byBiZSBlbmNvZGVk' 

或者用一个variables:

 >>> string = 'data to be encoded' >>> data = base64.b64encode(string.encode()) >>> print(data) b'ZGF0YSB0byBiZSBlbmNvZGVk' 

更长的答案:在Python 3 Base-64编码

base64编码采用6位二进制块,并使用字符AZ,az,0-9,“+”,“/”和“=”(某些编码使用不同的字符代替“+”和“/” )。 这是一个字符编码,它是基于基数-64或基数-64数字系统的math结构,但它们是非常不同的。 math中的Base-64是一个二进制或十进制的数字系统,你可以在整个数字上做这个基数的变化,或者(如果你正在转换的基数是2的幂小于64)剩下。

base64编码中,翻译是从左到右完成的; 那些前64个字符是为什么它被称为base64 编码 。 第65个'='符号用于填充,因为编码拉取了6位块,但是通常要编码的数据是8位字节,所以有时最后一个块中只有2或4位。

例:

 >>> data = b'test' >>> for byte in data: ... print(format(byte, '08b'), end=" ") ... 01110100 01100101 01110011 01110100 >>> 

如果你将这个二进制数据解释为一个整数,那么这就是你将它转换为base-10和base-64(base-64的表 )的方式:

 base-2: 01 110100 011001 010111 001101 110100 (base-64 grouping shown) base-10: 1952805748 base-64: B 0 ZXN 0 

然而, base64 编码会重组这些数据:

 base-2: 011101 000110 010101 110011 011101 00(0000) <- pad w/zeros to make a clean 6-bit chunk base-10: 29 6 21 51 29 0 base-64: d GV zd A 

所以,'B0ZXN0'是我们的二进制的基础版本,在math上讲。 但是, base64 编码必须在相反的方向上进行编码(所以原始数据被转换为“dGVzdA”),并且还有一个规则来告诉其他应用程序末尾剩余多less空间。 这是通过用'='符号填充结尾来完成的。 所以,这个数据的base64编码是'dGVzdA ==',当这个数据被解码以使它与原始数据相匹配时,用两个'='符号来表示两对比特将需要从结尾去除。

我们来testing一下,看看我是不是不诚实:

 >>> encoded = base64.b64encode(data) >>> print(encoded) b'dGVzdA==' 

现在到你的问题的关键:在Python 3中, str对象不是C风格的字符数组(所以它们不是字节数组),而是没有任何固有的或假定的编码的数据结构。 您可以用多种方式编码该string(或解释它)。 最常见的(在Python 3中是默认的)是UTF-8,特别是因为它与ASCII向后兼容(尽pipe和最广泛使用的编码一样)。 这就是当你拿一个string并调用.encode()方法时发生的事情:Python正在用.encode() -8(默认编码)解释string。

为什么使用base64编码?

比方说,我必须通过电子邮件将某些数据发送给某人,例如:

 >>> data = b'\x04\x6d\x73\x67\x08\x08\x08\x20\x20\x20' >>> print(data.decode()) >>> print(data) b'\x04msg\x08\x08\x08 ' >>> 

我种了两个问题:

  1. 如果我试图在Unix中发送这封电子邮件, \x04读取了\x04字符,电子邮件就会发送,因为这是用于END-OF-TRANSMISSION (Ctrl-D)的ASCII码,所以剩下的数据将不在传输。
  2. 另外,当我直接打印数据的时候,Python足够聪明地转义所有的邪恶控制字符,当这个string被解码为ASCII时,你可以看到'msg'不存在。 那是因为我使用了三个BACKSPACE字符和三个SPACE字符来擦除“味精”。 因此,即使我没有EOF字符,最终用户也不能从屏幕上的文本转换为真实的原始数据。

这只是一个演示,告诉你如何简单地发送原始数据。 将数据编码为base64格式将为您提供完全相同的数据,但格式可确保通过电子邮件等电子媒体进行发送。

如果要编码的数据包含“异国情调”的字符,我认为你必须编码为“UTF-8”

 encoded = base64.b64encode (bytes('data to be encoded', "utf-8")) 

有你需要的一切:

 expected bytes, not str 

前导b使你的string变成二进制。

你使用什么版本的Python? 2.x还是3.x?

编辑:请参阅http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit了解Python中string的细节3.X

如果string是unicode,最简单的方法是:

 import base64 a = base64.b64encode(bytes(u'complex string: ñáéíóúÑ', "utf-8")) b = base64.b64decode(a).decode("utf-8", "ignore") print(b)