为什么我需要'b'用Base64编码字符串?

python示例之后,我将一个字符串编码为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

为什么会这样?

519286 次浏览

这是你所需要的:

expected bytes, not str

前导b使字符串变成二进制。

你用什么版本的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 3.x中字符串的血腥细节

base64编码接受8位二进制字节数据,并仅使用字符A-Za-z0-9+/*进行编码,因此它可以通过不保存所有8位数据的通道传输,例如电子邮件。

因此,它需要一个8位字节的字符串。你可以在Python 3中使用b''语法创建它们。

如果你移除b,它就变成了一个字符串。字符串是Unicode字符序列。base64不知道如何处理Unicode数据,它不是8位的。事实上,它并不是真正的比特。: -)

在第二个例子中:

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

所有字符都完全符合ASCII字符集,因此base64编码实际上有点毫无意义。你可以用

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

或者更简单:

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

在这种情况下也是一样的。


*大多数base64类型也可能在结尾包含=作为填充。此外,一些base64变体可能使用+/以外的字符。有关概述,请参阅Wikipedia中的变量汇总表

如果要编码的数据包含“外来”字符,我认为您必须以“UTF-8”编码。

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

如果字符串是Unicode,最简单的方法是:

import base64


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


# a: b'Y29tcGxleCBzdHJpbmc6IMOxw6HDqcOtw7PDusOR'


b = base64.b64decode(a).decode("utf-8", "ignore")


print(b)
# b :complex string: ñáéíóúÑ

简短的回答

你需要将一个bytes-like对象(bytesbytearray等)推入base64.b64encode()方法。这里有两种方法:

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

或者用一个变量:

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

为什么?

在Python 3中,str对象不是c风格的字符数组(因此它们是字节数组),而是没有任何固有编码的数据结构。您可以以多种方式对该字符串进行编码(或解释)。最常见的(Python 3中的默认值)是utf-8,特别是因为它向后兼容ASCII(尽管,大多数广泛使用的编码)。这就是当你取string并在其上调用.encode()方法时所发生的情况:Python以utf-8(默认编码)解释字符串,并为你提供它对应的字节数组。

Python中的Base-64编码

最初题目问的是Base-64编码。继续阅读有关Base-64的内容。

base64 encoding接受6位二进制块,并使用字符A-Z, A-Z, 0-9, '+', '/'和'='对它们进行编码(一些编码使用不同的字符来代替'+'和'/')。这是一种基于64基或64进制数字系统的数学结构的字符编码,但它们非常不同。在数学中,Base-64是一种类似二进制或十进制的数字系统,你可以对整个数字进行基数的改变,或者(如果你要转换的基数是小于64的2次方)从右到左的块。

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      Z      X      N      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       G       V      z      d      A

所以,从数学上讲,'B0ZXN0'是二进制的64进制版本。然而,base64 编码必须以相反的方向进行编码(因此原始数据被转换为'dGVzdA'),并且还有一个规则告诉其他应用程序在结束时留下了多少空间。这是通过填充'='符号来完成的。因此,此数据的base64编码为'dGVzdA==',其中有两个'='符号,表示当此数据被解码以使其与原始数据匹配时,需要从末尾删除两对位。

让我们来测试一下,看看我是否不诚实:

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

为什么使用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字符时立即发送,因为这是END-OF-TRANSMISSION的ASCII (Ctrl-D),因此剩余的数据将被排除在传输之外。
  2. 此外,虽然Python足够聪明,可以在我直接打印数据时转义所有邪恶的控制字符,但当该字符串被解码为ASCII时,您可以看到'msg'不在那里。这是因为我使用了三个BACKSPACE字符和三个SPACE字符来删除'msg'。因此,即使我没有EOF字符,最终用户也无法将屏幕上的文本转换为真实的原始数据。

这只是一个演示,向您展示简单地发送原始数据有多么困难。将数据编码为base64格式可以得到完全相同的数据,但格式可以确保通过电子媒体(如电子邮件)发送数据是安全的。