了解Python对base64解码字节的表示

时间:2019-03-19 13:36:40

标签: python node.js python-3.x base64

上下文: 我正在尝试捕获某些MQTT消息,然后将其记录的程序。在执行此操作时,遇到一个奇怪的问题,需要帮助。

在传入的消息中,我得到一个以base64编码的字符串。我的程序尝试对二进制数据进行解码和解析,以找出数据包的某些标头。我的代码如下所示:

result = base64.standard_b64decode("AO/Nq4lnRSMBZXMnLHcKXhSObYxiFvY=")

结果的输出如下:

b"\x00\xef\xcd\xab\x89gE#\x01es',w\n^\x14\x8em\x8cb\x16\xf6"

如果使用nodeJS实现执行相同的操作,则输出将大不相同:

<Buffer 00 ef cd ab 89 67 45 23 01 65 73 27 2c 77 0a 5e 14 8e 6d 8c 62 16 f6>

我阅读了堆栈溢出中的其他一些链接,但不明白为什么存在这种差异。链接NodeJS base64 Vs Python base64的有效点指向已完成的编码,因此Python在这里所做的工作没有错。

进一步阅读后,我发现了另一个技巧,其中使用了binascii函数。因此,如果应用此逻辑,我的输出看起来就像NodeJS的输出!

import binascii
binascii.hexlify(result)
b'00efcdab89674523016573272c770a5e148e6d8c6216f6'

现在我的输出看起来像我想要的。但是,还有另一个新问题。 base64解码的输出为b'\ x00'格式,而hexlify的输出为b'0'。由于存在这种差异,因此我无法运行代码的另一部分,该部分将逐个字节地拆分此输出,以根据数据包的标头以不同的格式执行struct.unpack。

我能得到什么帮助吗?

2 个答案:

答案 0 :(得分:1)

如果我理解得很好,您会对字节的Python格式感到困惑。

基本上,当在Python中显示一个字节时,如果此字节与ASCII字符匹配,则使用该字符代替数字值。结果在Python和NodeJS中实际上是相同的,只是表示形式有所不同。

您可以通过对齐两个表示来检查它:

b"    \x00\xef\xcd\xab\x89  g  E  #\x01  e  s  '  ,  w \n  ^\x14\x8e  m\x8c  b\x16\xf6"
<Buffer 00  ef  cd  ab  89 67 45 23  01 65 73 27 2c 77 0a 5e  14  8e 6d  8c 62  16  f6>

如您所见,除了:

67 -> g
45 -> E
23 -> #
65 -> e
73 -> s
27 -> '
2c -> ,
0a -> \n
5e -> ^
6d -> m

事实是,上面显示的每个十六进制值都与ASCII表中的一个字符匹配。

您可以在Python解释器中轻松地对其进行验证(ord提供给定字符的ascii代码,hex将其转换为十六进制表示形式):

>>> hex(ord('g'))
'0x67'
>>> hex(ord('E'))
'0x45'
>>> hex(ord('#'))
'0x23'

最后,您甚至可以在几行Python中获得与NodeJS完全相同的表示形式:

>>> bytes = b"\x00\xef\xcd\xab\x89gE#\x01es',w\n^\x14\x8em\x8cb\x16\xf6"
>>> print('<Buffer {}>'.format(' '.join([format(c, '02x') for c in bytes])))
<Buffer 00 ef cd ab 89 67 45 23 01 65 73 27 2c 77 0a 5e 14 8e 6d 8c 62 16 f6>

答案 1 :(得分:0)

base64.standard_b64decode返回一个bytes对象,该对象是不可变的单字节序列。这由b之前的"字母表示。

来自docs

  

字节字面量始终以'b''B'为前缀;它们产生bytes类型的实例,而不是str类型的实例。它们只能包含ASCII字符;数值等于或大于128的字节必须用转义符表示。

您在这里看到的是bytes对象的ASCII表示形式,而不是字符串。

要将bytes对象转换为类似于从nodeJS得到的结果的字符串,可以使用bytes对象方法hex

import base64

result = base64.b64decode(b"AO/Nq4lnRSMBZXMnLHcKXhSObYxiFvY=")

print(result.hex())

>>> 00efcdab89674523016573272c770a5e148e6d8c6216f6