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

dub*_*ech 229 python base64 python-3.x

在这个python示例之后,我将字符串编码为Base64,其中:

>>> import base64
>>> encoded = base64.b64encode(b'data to be encoded')
>>> encoded
b'ZGF0YSB0byBiZSBlbmNvZGVk'
Run Code Online (Sandbox Code Playgroud)

但是,如果我省略领先b:

>>> encoded = base64.b64encode('data to be encoded')
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

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
Run Code Online (Sandbox Code Playgroud)

为什么是这样?

Len*_*bro 248

base64编码需要8位二进制字节数据和编码它仅使用字符A-Z,a-z,0-9,+,/*所以它可以在不保留任何数据,例如电子邮件的所有8位信道来传输.

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

如果删除它b,它就变成了一个字符串.字符串是一系列Unicode字符.base64不知道如何处理Unicode数据,它不是8位.事实上,这并不是真的.:-)

在你的第二个例子中:

>>> encoded = base64.b64encode('data to be encoded')
Run Code Online (Sandbox Code Playgroud)

所有字符都巧妙地适合ASCII字符集,因此base64编码实际上有点无意义.您可以将其转换为ascii

>>> encoded = 'data to be encoded'.encode('ascii')
Run Code Online (Sandbox Code Playgroud)

或者更简单:

>>> encoded = b'data to be encoded'
Run Code Online (Sandbox Code Playgroud)

在这种情况下哪个是相同的.


*大多数base64风味最后也可能包括一个=填充.此外,某些base64变体可能使用除+和之外的字符/.有关概述,请参阅Wikipedia上的Variants摘要表.


Gre*_*ica 155

简答

你需要一个推bytes-like对象(bytes,bytearray,等)的base64.b64encode()方法.这有两种方式:

>>> data = base64.b64encode(b'data to be encoded')
>>> print(data)
b'ZGF0YSB0byBiZSBlbmNvZGVk'
Run Code Online (Sandbox Code Playgroud)

或者使用变量:

>>> string = 'data to be encoded'
>>> data = base64.b64encode(string.encode())
>>> print(data)
b'ZGF0YSB0byBiZSBlbmNvZGVk'
Run Code Online (Sandbox Code Playgroud)

为什么?

在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,'+','/'和'='对它们进行编码(某些编码使用不同的字符代替'+'和'/') .这是一个基于radix-64或base-64数字系统的数学结构的字符编码,但它们是非常不同的.数学中的Base-64是一个数字系统,如二进制或十进制,你可以在整个数字上进行基数更改,或者(如果你转换的​​基数是2的幂小于64)从右到右的块剩下.

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

例:

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

如果您将该二进制数据解释为单个整数,那么您可以将其转换为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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

因此,'B0ZXN0'是我们二进制的base-64版本,从数学上讲.但是,base64 编码必须以相反的方向进行编码(因此原始数据转换为'dGVzdA'),并且还有一条规则告诉其他应用程序在结尾处剩余多少空间.这是通过用'='符号填充末尾来完成的.因此,base64该数据的编码是'dGVzdA ==',其中两个'='符号表示当该数据被解码以使其与原始数据匹配时,将需要从末尾移除两对比特.

让我们测试一下,看看我是不是在做不诚实:

>>> encoded = base64.b64encode(data)
>>> print(encoded)
b'dGVzdA=='
Run Code Online (Sandbox Code Playgroud)

为何使用base64编码?

假设我必须通过电子邮件向某人发送一些数据,例如:

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

>>> print(data)
b'\x04msg\x08\x08\x08   '
>>>
Run Code Online (Sandbox Code Playgroud)

我种了两个问题:

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

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

  • `base64.b64encode(s.encode()).decode()`当你想要的只是一个字符串到字符串的转换时,它不是非常pythonic.`base64.encode(s)`至少在python3中应该足够了.感谢python中有关字符串和字节的非常好的解释 (4认同)
  • @MortenB 顺便说一句,`base64.encode(s)` 在 Python3 中不起作用;你是说应该有这样的东西吗?我认为它可能令人困惑的原因是,根据字符串的编码和内容,`s` 可能没有 1 个作为字节数组的唯一表示。 (4认同)
  • @MortenB 是的,这很奇怪,但从好的方面来说,只要工程师知道字节数组和字符串数组之间的区别,就会很清楚发生了什么,因为它们之间没有单一映射(编码),就像其他语言一样认为。 (3认同)
  • @MortenB,但 b64 不仅仅适用于文本,任何二进制内容都可以进行 b64 编码(音频、图像等)。在我看来,使其按照您的建议工作会进一步隐藏文本和字节数组之间的差异,从而使调试变得更加困难。它只是将困难转移到其他地方。 (2认同)

Ale*_*ecz 31

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

encoded = base64.b64encode (bytes('data to be encoded', "utf-8"))
Run Code Online (Sandbox Code Playgroud)


alf*_*era 17

如果字符串是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: ñáéíóúÑ
Run Code Online (Sandbox Code Playgroud)


小智 12

有你需要的一切:

expected bytes, not str
Run Code Online (Sandbox Code Playgroud)

领先b使你的字符串二进制.

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

编辑:有关Python中字符串的详细信息,请参阅http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit 3.X

  • @dublintech因为(unicode)文本与原始数据不同.如果你想在Base64中编码一个文本字符串,首先需要确定字符编码(如UTF-8)然后你有字节而不是字符,你可以用文本ascii-safe形式编码. (2认同)
  • 这不能回答问题。他知道它适用于字节对象,但不适用于字符串对象。问题是*为什么*。 (2认同)