将ndarray.tofile()和numpy.fromfile()与FIFO一起使用时,“获取文件位置失败”

时间:2019-04-28 10:36:40

标签: python python-3.x numpy fifo

我正在编写一些伪代码来学习FIFO在python中的工作方式(并在以后的项目中使用它们)。当我尝试写入或读取时,出现“ OSError:获取文件位置失败”消息。

我正在尝试在两个python代码之间传输复杂的数据。我正在使用FIFO,因为我需要更多不同的通道来在运行的模块之间进行通信。我正在使用bash脚本运行它们,您可以在下面看到。

    #first.py
    import numpy as np


    data = np.complex64([1, 2, 3])

    fifo = open("fifoka", "wb")

    data.tofile(fifo)

    fifo.flush()
    fifo.close()
    #second.py
    import numpy as np

    fifo = open("fifoka", "rb")
    data = np.fromfile(fifo, dtype=np.complex64)
    fifo.close()

    print(data)
    #!/bin/bash
    mkfifo fifoka

    python3 first.py | \
    python3 second.py

    rm fifoka

如果我使用fifo.write(data.tobytes())而不是data.tofile(fifo),那么它可以正常工作,但是根据spec,它应该以相同的方式工作。

当我尝试从同一fifo读取内容时,我遇到了同样的问题,所以我认为我在犯同样的错误。

所以我的问题是,在这种情况下如何正确使用np.fromfile()ndarray.tofile()

3 个答案:

答案 0 :(得分:1)

似乎是numpy中的缺陷。我只是碰到了将一些代码从未知的numpy版本Py2.7迁移到numpy 1.17.4的Py3.8.2。将arr.tofile(fout)更改为fout.write(arr.tobytes())就可以了。

更好的解决方案:如果要写入的数组是连续的,请使用fout.write(memoryview(arr)),这样可以避免复制。下面的实用程序将尽可能使用memoryview,否则使用tobytes

def arr_tofile(a,outfile):
    if a.flags['C_CONTIGUOUS']:
        b = memoryview(a)
    else:
        b = a.tobytes()
    outfile.write(b)

十多年来,我一直在通过管道使用arr.tofile(f)将数据发送到2.x下的其他进程,所以这是一个新缺陷,也许特定于python 3.x?

文档确实说tofile绕过了write方法并使用了fd,但确实没有理由寻求或说明要执行tofile,没有理由在管道上不起作用。

更新-由于存在兼容性问题,它在python3中不起作用

对于python2操作,tofile中的过程是从python FILE*对象获取file的;然后刷新该文件,获取基础文件号,然后使用PyArray_ToFile函数对其进行写入。

显然,由于文件对象中添加了内部缓冲,这被Python3打破了。

所以现在,它可以通过调用npy_PyFile_Dup2从python文件对象中创建一个FILE*来工作,然后使用函数{从该FILE*到fd进行写操作{1}}。最后,PyArray_ToFile用于关闭新文件,并将原始文件查找到相同位置。对于python2,npy_PyFile_DupClose2(file, fd, orig_pos)npy_PyFile_Dup2基本上定义为存根;如上所述,使用了一个基础npy_PyFile_DupClose2

在Python3中,它们可以做更多的事情。我并没有全部讲完,但是FILE*实际上是使用python机制调用npy_PyFile_Dup2来创建新的文件句柄,并且在写完之后,os.dup对新文件,然后在原始python文件上进行“搜索”以跳过刚写在另一个句柄上的数据。

底线-这有点混乱-在python3下,最好避免使用npy_PyFile_DupClose2,即使它起作用,除非写入的大小足够大以至于证明相当大的开销是合理的。而且它根本无法在不可搜索的文件上运行。在两种情况下,都改用arr.tofile(file)file.write(memoryview(arr))

明显的下一个问题-这个问题可以解决在管道上使用吗?

也许-它将依赖于能够检测到输出是管道,并且在这种情况下,将刷新python文件对象并继续写入其文件句柄(如python 2方法一样)。写入管道时,无需在输出文件上支持后续的“告诉”。


https://github.com/numpy/numpy/blob/2f70544179e24b0ebc0263111f36e6450bbccf94/doc/source/release/1.8.1-notes.rst

弃用

C-API

实用程序函数npy_PyFile_Dup和npy_PyFile_DupClose被内部缓冲python 3破坏,适用于其文件对象。要修复此问题,请在npy_3kcompat.h中声明npy_PyFile_Dup2和npy_PyFile_DupClose2,并弃用旧功能。由于这些函数的脆弱性,建议在可能的情况下改为使用python API。


https://github.com/numpy/numpy/blob/382758355998951cea2b9f6ad1fb83e7dc4c3a02/numpy/core/src/multiarray/methods.c

file.write(arr.tobytes())

https://github.com/numpy/numpy/blob/64fb290a8cb8fa9201f18015f3de1186e950a137/numpy/core/include/numpy/npy_3kcompat.h

PyObject *file = (from param)
FILE *fd;
npy_off_t orig_pos = 0;  

fd = npy_PyFile_Dup2(file, "wb", &orig_pos);
if (fd == NULL) {
    goto fail;
}
if (PyArray_ToFile(self, fd, sep, format) < 0) {
    goto fail;
}
if (npy_PyFile_DupClose2(file, fd, orig_pos) < 0) {
    goto fail;
}
if (own && npy_PyFile_CloseFile(file) < 0) {
    goto fail;
}
Py_DECREF(file);
Py_RETURN_NONE;

...

/*
 * Get a FILE* handle to the file represented by the Python object
 */
static NPY_INLINE FILE*
npy_PyFile_Dup2(PyObject *file, char *mode, npy_off_t *orig_pos)
{
    int fd, fd2, unbuf;
    PyObject *ret, *os, *io, *io_raw;
    npy_off_t pos;
    FILE *handle;

    /* For Python 2 PyFileObject, use PyFile_AsFile */
#if !defined(NPY_PY3K)
    if (PyFile_Check(file)) {
        return PyFile_AsFile(file);
    }
#endif

    /* Flush first to ensure things end up in the file in the correct order */

    [[[... continue to call os.dup via python interface....]]]
}

答案 1 :(得分:0)

我认为这是由于numpy.ndarray.tofile的工作原理所致。

from the docs

当fid是文件对象时,数组内容直接写入到 文件,绕过文件对象的write方法。结果,文件 不能与支持压缩的文件对象一起使用(例如, GzipFile)或不支持fileno()的类似文件的对象(例如, BytesIO)

答案 2 :(得分:0)

不可能在FIFO中查找。

在系统调用级别,lseek(2)也用于获取文件位置。所以那也不行。