如何在python中从另一个进程读取管道上的二进制数据?

时间:2018-02-10 20:47:06

标签: python-3.x io subprocess

我使用以下命令启动另一个进程:

p = subprocess.Popen(binFilePath, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=False)

根据文档p.stdout现在应该有一个二进制数据流,因为universal_newlines设置为False

如果其他程序现在发送二进制数据,我该怎么读?虽然等待读取的数据量有限,但不会返回对以下命令的调用:

returnedData = p.stdout.read()

我想要在管道中等待的数据量(如果有数据可用,否则阻塞直到数据可用)。那我该怎么做呢?

1 个答案:

答案 0 :(得分:0)

这并不简单,因为python不像其他编程语言那样设计用于此类内容。

首先:您必须将管道切换为非阻塞。否则,对read()的调用将在所有情况下都会阻止。这是通过以下代码完成的:

fd = p.stdout.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

注意:请注意p.stdout不是输出到其他进程的输出流,而是进程的输出流,即输入流。

现在我们有了非阻塞流,我们可以继续。

第二名:等到数据可用。这可以通过select()

完成
streams = [ p.stdout ]
temp0 = []
readable, writable, exceptional = select.select(streams, temp0, temp0, 5)
if len(readable) == 0:
    raise Exception("Timeout of 5 seconds reached!")

据我所知,exceptional将永远不会收到任何数据,因为我们在这里处理管道。

第三:现在让我们读取数据:

temp = bytearray(4096)
numberOfBytesReceived = p.stdout.readinto(temp)
if numberOfBytesReceived <= 0:
    raise Exception("No data received!")

其他信息:

当然你不知道发件人实际发送了多少数据。您必须重复读取数据并检查是否已完成发送过程的所有输出。这可以确定进程是否关闭了流 - 但这会使这个问题完全过时,因为根本不需要这种I / O实现 - 或者直到某些特定的所有数据-sent -mark已发送。

附加说明:

如果您需要对管道执行多次读取以便完全读取由另一个进程发送的有意义的数据块,您将最终在循环中执行此操作并将数据附加到缓冲区。这需要将您p.stout.readinto(temp)已写入的临时缓冲区中的数据复制到要保留数据的真实缓冲区。据我所知,python中没有更有效的方法,因为readinto()始终(!)写入预分配缓冲区的开头。由于在其他编程语言中众所周知,因此无法在特定偏移处写入数据。如果在我看来真的没有其他方式,那么这必须被认为是python API中的设计缺陷。