如何在没有tmp存储的情况下将二进制数据传输到numpy数组?

K.-*_*Aye 7 python stdin numpy stringio

有几个类似的问题,但没有一个直接回答这个简单的问题:

如何捕获命令输出并将该内容流式传输到numpy数组而不创建要读取的临时字符串对象?

那么,我想做的是:

import subprocess
import numpy
import StringIO

def parse_header(fileobject):
    # this function moves the filepointer and returns a dictionary
    d = do_some_parsing(fileobject)
    return d

sio = StringIO.StringIO(subprocess.check_output(cmd))
d = parse_header(sio)
# now the file pointer is at the start of data, parse_header takes care of that.
# ALL of the data is now available in the next line of sio
dt = numpy.dtype([(key, 'f8') for key in d.keys()])

# i don't know how do make this work:
data = numpy.fromxxxx(sio , dt)

# if i would do this, I create another copy besides the StringIO object, don't I?
# so this works, but isn't this 'bad' ?
datastring = sio.read()
data = numpy.fromstring(datastring, dtype=dt)
Run Code Online (Sandbox Code Playgroud)

我尝试使用StringIO和cStringIO,但numpy.frombuffer和numpy.fromfile都不接受它们.

使用StringIO对象我首先要将流读入字符串然后使用numpy.fromstring,但我想避免创建中间对象(几千兆字节).

对我来说,另一种选择是如果我可以将sys.stdin流式传输到numpy数组中,但这对numpy.fromfile也不起作用(seek需要实现).

这有什么解决方法吗?我不能成为第一个尝试这个的人(除非这是一个PEBKAC案例?)

解决方案:这是当前的解决方案,它是unutbu指令的混合,如何使用PIPE的Popen和eryksun使用bytearray的提示,所以我不知道接受谁!?:S

proc = sp.Popen(cmd, stdout = sp.PIPE, shell=True)
d = parse_des_header(proc.stdout)
rec_dtype = np.dtype([(key,'f8') for key in d.keys()])
data = bytearray(proc.stdout.read())
ndata = np.frombuffer(data, dtype = rec_dtype)
Run Code Online (Sandbox Code Playgroud)

我没有检查数据是否真的没有创建另一个副本,不知道如何.但是我注意到这比我之前尝试的所有内容都快得多,所以非常感谢答案的作者!

Ery*_*Sun 5

您可以使用Popenstdout=subprocess.PIPE。阅读标题,然后将其余部分加载到中bytearray以与一起使用np.frombuffer

根据您的修改的其他评论:

如果您要拨打电话proc.stdout.read(),则等同于使用check_output()。两者都创建一个临时字符串。如果预分配data,则可以使用proc.stdout.readinto(data)。然后,如果读入的字节数data少于len(data),则释放多余的内存,否则扩展data剩下的要读取的内容。

data = bytearray(2**32) # 4 GiB
n = proc.stdout.readinto(data)
if n < len(data):
    data[n:] = ''        
else:
    data += proc.stdout.read()
Run Code Online (Sandbox Code Playgroud)

您也可以从预先分配ndarray ndata和使用开始buf = np.getbuffer(ndata)。然后readinto(buf)如上所述。

这是一个示例,显示了在bytearray和 之间共享内存np.ndarray

>>> data = bytearray('\x01')
>>> ndata = np.frombuffer(data, np.int8)
>>> ndata
array([1], dtype=int8)
>>> ndata[0] = 2
>>> data
bytearray(b'\x02')
Run Code Online (Sandbox Code Playgroud)