Python/ctypes文件处理Mac OS X和Ubuntu之间的差异

eba*_*arr 2 python ctypes

我目前正在尝试在我的linux机器(Ubuntu 12.04.1 LTS)和我的新Mac(OS X 10.7.4)之间移植一些代码,当我使用python的ctypes模块访问C标准库时,我遇到了一些令人困惑的行为Mac.

为了说明问题,以下是一个最小的例子:

import ctypes as C
import numpy as np

libc = C.CDLL("/usr/lib/libc.dylib")   #/usr/lib/libc.so.6 on ubuntu

np.arange(10,dtype="ubyte").tofile("test.bin") # create some test data

buffer_array = np.empty(10,dtype="ubyte") # create a reading buffer

buffer_array_c = np.ctypeslib.as_ctypes(buffer_array) # get the ctypes version of the buffer 

c_file = libc.fopen("test.bin","r") # open the file through libc   

libc.fread(buffer_array_c, 1, 10, c_file) # read from the file

libc.fclose(c_file)

print "Desired output:"
print np.fromfile("test.bin",dtype="ubyte")
print
print "Actual output:"
print buffer_array
Run Code Online (Sandbox Code Playgroud)

在Linux上,这可以按预期工作,产生以下内容:

Desired output:
[0 1 2 3 4 5 6 7 8 9]

Actual output:
[0 1 2 3 4 5 6 7 8 9]
Run Code Online (Sandbox Code Playgroud)

然而,在Mac上,我只得到"Segmentation fault:11".

我已经尝试了一下,用以下方法交换fopen调用:

py_file = open("test.bin","r")

c_file = C.pythonapi.PyFile_AsFile(C.py_object(py_file))
Run Code Online (Sandbox Code Playgroud)

哪个适用于Linux但不适用于Mac.

我认为问题来自于使用c_file调用fread,好像我编写了一个最小的C函数来打开文件,然后使用先前分配的缓冲区调用fread,代码按预期执行.

我通常不是Mac用户,所以问题可能很明显,但任何帮助都会非常有用.

作为参考,我正在使用:

Python 2.7.3,Numpy 1.4.0和Ctypes 1.1.0

编辑:

为了给出这个上下文,我正在尝试使用快速方法将非常大的二进制文件(~40-200 GB)逐个读取到python中.正如评论者指出的那样,直接访问标准库fread和fwrite函数并没有真正提高性能.这是事实,但我很困惑为什么.如果我使用numpy.fromfile来读取块中的大文件,那么每次读取都不会创建新的内存分配吗?

解:

问题似乎源于文件句柄存储的64位/ 32位差异.解决方案只是在使用之前显式设置每个c函数的restype和argtypes.

即在64位机器上,我们在C.CDLL调用之后放置它:

lib.fopen.restype = C.c_long
lib.fread.argtypes = [C.c_void_p, C.c_size_t, C.c_size_t, C.c_long]
lib.fclose.argtypes = [C.c_long]
Run Code Online (Sandbox Code Playgroud)

在32位机器上:

lib.fopen.restype = C.c_int
lib.fread.argtypes = [C.c_void_p, C.c_size_t, C.c_size_t, C.c_int]
lib.fclose.argtypes = [C.c_int]
Run Code Online (Sandbox Code Playgroud)

Arm*_*igo 6

您是在尝试使用32位Ubuntu还是64位OS/X?我认为问题是你的libc.fopen()版本返回一个C"int",它几乎总是一个32位的值---但真正的fopen()返回一个指针.因此,在64位操作系统上,您获得的c_file将被截断为32位整数.在32位操作系统上,它仍然可以工作,因为32位整数可以传递回fread()和fclose(),它将再次将其解释为指针.要修复它,您需要声明libc.fopen()的重定义类型.

(我只能推荐使用CFFI替代ctypes并使用更健全的默认值,但当然我偏爱它,作为作者之一:-)