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

2024-04-26 07:45:39 发布

您现在位置:Python中文网/ 问答频道 /正文

在这个python example之后,我将字符串编码为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

这是为什么?


Tags: to字符串indatabytesexamplelinenot
3条回答

简短的回答

您需要将bytes-like对象(bytesbytearray等)推送到base64.b64encode()方法。有两种方法:

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

或使用变量:

>>> 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 3中的Base-64编码

最初的问题标题是关于Base-64编码的。继续读一些关于基础64的东西。

base64编码采用6位二进制块,并使用字符A-Z、A-Z、0-9、“+”、“/”和“=”(有些编码使用不同的字符来代替“+”和“/”)。这是一种基于基数64或基数64数字系统的数学构造的字符编码,但它们有很大的不同。数学中的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(table for 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   '
>>>

我有两个问题:

  1. 如果我尝试在Unix中发送该电子邮件,则该电子邮件将在读取\x04字符后立即发送,因为这是END-OF-TRANSMISSION(Ctrl-D)的ASCII,因此剩余的数据将被排除在传输之外。
  2. 另外,虽然Python足够聪明,在我直接打印数据时可以避开所有邪恶的控制字符,但当该字符串被解码为ASCII时,您可以看到“msg”不在那里。那是因为我用了三个BACKSPACE字符和三个SPACE字符来删除“msg”。因此,即使我没有EOF字符,最终用户也无法将屏幕上的文本转换为真实的原始数据。

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

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

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

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

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

如果删除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变体可能使用+/以外的字符。有关概述,请参见维基百科上的Variants summary table

相关问题 更多 >