为什么我需要使用“b”来使用Base64编码字符串?
为什么我需要使用“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 "", line 1, in File "C:\Python32\lib\base64.py", line 56, in b64encode raise TypeError("expected bytes, not %s" % s.__class__.__name__) TypeError: expected bytes, not str
为什么会这样呢?
简短回答
您需要将bytes-like
对象(bytes
, bytearray
,等等)推送到 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风格的字符数组(因此它们不是字节数组),而是没有任何固有编码的数据结构。您可以以各种方式对该字符串进行编码(或解释)。最常见的是utf-8(在Python 3中是默认值),特别是因为它与ASCII向后兼容(虽然大多数使用广泛的编码方式都是)。当您拿到一个string
并对其调用.encode()
方法时,这就是发生了什么:Python正在解释该字符串的utf-8(默认编码),并为您提供它对应的字节数组。
Python 3中的Base-64编码
最初,问题标题问的是Base-64编码。继续阅读有关Base-64的内容。
base64
编码使用A-Z,a-z,0-9,'+','/'和'='(某些编码在'+'和'/'的位置使用不同的字符)来对6位二进制块进行编码。这是基于基数64或Base-64数字系统的数学构造的字符编码,但它们非常不同。Base-64在数学上是像二进制或十进制之类的数字系统,您可以对整个数字或(如果您从的基数是小于64的2的幂)从右到左在块中执行该基数更改。
在 base64
编码中,转换是从左往右进行的;前 64 个字符是为什么它称为 base64
编码 的原因。第 65 个 '=' 符号用于填充,因为编码会提取 6 位的块,但通常要编码的数据是 8 位的字节,因此最后一个块中可能只有两个或 4 个位。
例如:
>>> data = b'test' >>> for byte in data: ... print(format(byte, '08b'), end=" ") ... 01110100 01100101 01110011 01110100 >>>
如果你将二进制数据解释为一个整数,那么这就是将其转换为十进制和 base64
(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' 是我们二进制数据的 base-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 ' >>>
我设置了两个问题:
- 如果我尝试在Unix中发送那封电子邮件,那么在读取到
\x04
字符时,邮件就会立即发送,因为这是ASCII码的END-OF-TRANSMISSION
(Ctrl-D),因此剩余的数据将被省略。 - 此外,虽然Python足够聪明,可以在直接打印数据时转义所有恶意的控制字符,但当该字符串解码为ASCII码时,可以看到该“msg”不存在。这是因为我使用了三个
BACKSPACE
字符和三个SPACE
字符来清除“msg”。因此,即使在没有EOF
字符的情况下,最终用户也无法将屏幕上的文本转换为真正的原始数据。
这只是一个演示,展示了简单发送原始数据是多么困难。将数据编码为base64格式可以获得完全相同的数据,但以确保对于电子媒体(如电子邮件)的发送是安全的格式。
base64编码将8位二进制字节数据编码为只使用字符A-Z
,a-z
,0-9
,+
,/
的字符串,因此可以在不保留所有8位数据的通道上传输,例如电子邮件。
因此,它需要一串8位字节。在Python 3中,您可以使用b''
语法创建这些字节。
如果您删除b
,它将变成一个字符串。字符串是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变种的结尾也可能包括=
作为填充。此外,一些base64变种可能使用除+
和/
之外的字符。请参见维基百科上的变种汇总表,以获得概述。