我正在编写一些伪代码来学习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()
。
答案 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方法一样)。写入管道时,无需在输出文件上支持后续的“告诉”。
弃用
C-API
实用程序函数npy_PyFile_Dup和npy_PyFile_DupClose被内部缓冲python 3破坏,适用于其文件对象。要修复此问题,请在npy_3kcompat.h中声明npy_PyFile_Dup2和npy_PyFile_DupClose2,并弃用旧功能。由于这些函数的脆弱性,建议在可能的情况下改为使用python API。
file.write(arr.tobytes())
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的工作原理所致。
当fid是文件对象时,数组内容直接写入到 文件,绕过文件对象的write方法。结果,文件 不能与支持压缩的文件对象一起使用(例如, GzipFile)或不支持fileno()的类似文件的对象(例如, BytesIO)。
答案 2 :(得分:0)
不可能在FIFO中查找。
在系统调用级别,lseek(2)
也用于获取文件位置。所以那也不行。