为什么需要“b”来使用 Base64 编码字符串?
- 2025-02-14 09:50:00
- admin 原创
- 43
问题描述:
我遵循了文档中有关如何在 Python 中使用 Base64 编码的示例:
>>> import base64
>>> encoded = base64.b64encode(b'data to be encoded')
>>> encoded
b'ZGF0YSB0byBiZSBlbmNvZGVk'
但是,如果我尝试对普通字符串进行编码-省略前导b
:
>>> encoded = base64.b64encode('data to be encoded')
我得到了一个TypeError
。在旧版本的 Python 中它看起来像:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:Python32libase64.py", line 56, in b64encode
raise TypeError("expected bytes, not %s" % s.__class__.__name__)
TypeError: expected bytes, not str
在较新的版本中它可能看起来像:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.12/base64.py", line 58, in b64encode
encoded = binascii.b2a_base64(s, newline=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: a bytes-like object is required, not 'str'
为什么会发生这种情况?
解决方案 1:
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 变体可能使用除 和 之外的字符+
。/
请参阅维基百科上的变体摘要表以了解概述。
解决方案 2:
简短答案
您需要将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 样式的字符数组(因此它们不是字节数组),而是没有任何固有编码的数据结构。您可以用多种方式对该字符串进行编码(或解释它)。最常见的(也是 Python 3 中的默认)是 utf-8,特别是因为它向后兼容 ASCII(尽管,与最广泛使用的编码一样)。这就是当您获取string
并调用.encode()
它的方法时发生的事情:Python 正在以 utf-8(默认编码)解释字符串并为您提供它对应的字节数组。
Python 3 中的 Base-64 编码
最初问题标题询问的是 Base-64 编码。请继续阅读以了解 Base-64 的内容。
base64
编码采用 6 位二进制块,并使用字符 AZ、az、0-9、'+'、'/' 和 '=' 对其进行编码(某些编码使用不同的字符代替 '+' 和 '/')。这是一种基于 64 基数或 64 进制数字系统的数学构造的字符编码,但它们非常不同。数学中的 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
>>>
如果将该二进制数据解释为单个整数,则可以这样将其转换为十进制和 64 进制(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'x04x6dx73x67x08x08x08x20x20x20'
>>> print(data.decode())
>>> print(data)
b'x04msgx08x08x08 '
>>>
我种下的问题是两个:
如果我尝试在 Unix 中发送该电子邮件,则电子邮件会在
x04
读取字符后立即发送,因为这是END-OF-TRANSMISSION
(Ctrl-D) 的 ASCII,因此剩余数据将被排除在传输之外。此外,虽然 Python 足够聪明,可以在我直接打印数据时转义所有邪恶的控制字符,但当将该字符串解码为 ASCII 时,您会发现“msg”不存在。这是因为我使用了三个
BACKSPACE
字符和三个SPACE
字符来擦除“msg”。因此,即使我没有EOF
在那里使用字符,最终用户也无法将屏幕上的文本转换为真实的原始数据。
这只是一个演示,向您展示发送原始数据有多么困难。将数据编码为 base64 格式可为您提供完全相同的数据,但格式可确保通过电子邮件等电子媒体安全发送。
解决方案 3:
如果要编码的数据包含“外来”字符,我认为你必须用“UTF-8”进行编码
encoded = base64.b64encode (bytes('data to be encoded', "utf-8"))
解决方案 4:
如果字符串是 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: ñáéíóúÑ
解决方案 5:
这里有你需要的一切:
expected bytes, not str
前导b
使您的字符串成为二进制。
您使用哪个版本的 Python?2.x 还是 3.x?
编辑:有关 Python 3.x 中字符串的详细信息,请参阅http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit