Python base64编码然后解码通用对象

时间:2018-05-24 23:35:33

标签: python numpy serialization encoding base64

我正在尝试将numpy.ndarray转换为base64,然后将其转换回来。 base64库是否可行? 下面非常简单的代码甚至没有按预期工作。 我错过了什么?

import numpy as np

x = np.array([[1, 2, 3], [4, 5, 6]], np.int32)
print(x)
print(type(x))

encoded = base64.b64encode(x)
decoded = base64.b64decode(encoded)
print(decoded)
print(type(decoded))

有没有办法获得原始变量?

一般问题是: 我可以转换"任何"反对二进制字符串,然后转换回原始类型?

我也许可以使用pickle但我需要压缩格式(不在文件中):

之类的东西
x_compressed = zipped(pickle.dumps(x))

2 个答案:

答案 0 :(得分:3)

我不确定您要完成的任务,但您可以对任何具有bytes表示的对象进行base-64编码。在您给出的示例中,您将numpy数组编码为base64。

这是因为numpy数组有bytes形式。您可以通过在数组周围包裹bytes()或使用.tobytes()方法来覆盖它。

import numpy as np

x = np.array([1,2,3])

bytes(x)
# returns:
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'

x.tobytes()
# returns:
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'

由于我们有一个bytes表示的数组,您可以将它传递给base64编码器。请注意,如果对象不是类似于字节的对象,则base64将在编码之前尝试将其转换,如下例所示:

base64.b64encode(x)
# returns
b'AQAAAAIAAAADAAAA'

base64.b64encode(x.tobytes())
# returns
b'AQAAAAIAAAADAAAA'

字节数组没什么特别的。这只是一系列数字!就是这样。你没有恢复numpy数组的原因是因为编码解码过程仍然只是给你x.tobytes()而不是x本身的结果。

要获取原始对象,您需要一个可以读取字节序列并返回某种对象的接口。幸运的是,numpy可以通过frombuffer函数做到这一点。但是,你需要告诉numpy它读取的数组TYPE为字节。

换句话说,你可以有一个int32数组和一个int16数组,它们具有相同的字节表示,但要恢复正确的数组,你需要告诉numpy哪个TYPE是正确的。所以你需要某种对象的知识。

x = np.array([1,2,3])

# encode as base 64
x_64 = base64.b64encode(x.tobytes())

# decode back to bytes
x_bytes = base64.b64decode(x_64)

# use numpy to recreate original array of ints
np.frombuffer(x_bytes, dtype=int)
# returns:
np.array([1, 2, 3])

如果要保存对象然后稍后恢复,则该过程称为序列化。有两个非常好的包处理序列化,第一个在标准库中,调用pickle,第二个称为dill,可以处理更复杂的对象。

import pickle

x = np.array([1,2,3])
pickled_x = pickle.dumps(x)
# pickled_x is a bytes-object that is a hard to read by humans. 

pickle.loads(x)
# returns:
np.array([1, 2, 3])

答案 1 :(得分:1)

base64编码可能不是一个很好的选择。如果您需要在可能由于传统原因限制为ASCII的环境中存储或传输数据,这可能是一个不错的选择。

对于numpy数组,有更简单的方法tostringfrombuffer,需要注意的是你需要知道dtype和shape。如果您使用固定尺寸和类型,可以直接使用它:

>>> x
array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)
>>> x.tostring()
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00'
>>> np.frombuffer(x.tostring(), dtype=x.dtype).reshape(x.shape)
array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)

否则,只需使用numpy的序列化器和解串器np.savenp.load,它们将为您处理这些细节:

>>> import io
>>> f = io.BytesIO()
>>> np.save(f, x)
>>> f.getvalue()
b"\x93NUMPY\x01\x00v\x00{'descr': '<i4', 'fortran_order': False, 'shape': (2, 3), }                                                          \n\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
>>> f.seek(0); np.load(f)
array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)

注意:如果需要,您可以随时在这些序列化字节(即f.getvalue()字符串)上添加b64编码/解码传递 - 但出于所有实际目的,您可能只需要传输numpy的字节直接没有转换为/从b64。

  

一般问题是:我可以转换&#34;任何&#34;反对二进制字符串,然后转换回原始类型?

理论上,是的,因为无论如何,对象只是计算机上的字节。 对于Python对象的常规序列化,标准库提供pickle。可以使用zlib添加压缩,仅举一个例子。