从c字符数组创建PyString而不复制

时间:2014-07-31 19:53:37

标签: python c

我有一个来自C app的大型字符串缓冲区(基本上是12GB)。

我想在C中为嵌入式Python解释器创建PyString对象而不复制字符串。这可能吗?

2 个答案:

答案 0 :(得分:7)

我认为Python String对象嵌入进入PyObject结构的基本原因是不可能的。换句话说,Python字符串对象是PyObject_HEAD,后跟字符串的字节。您必须有足够的空间将PyObject_HEAD信息放在现有字节周围。

答案 1 :(得分:7)

没有副本就不能使用PyString,但可以使用ctypes。事实证明ctypes.c_char_p基本上像一个字符串。例如,使用以下C代码:

static char* names[7] = {"a", "b", "c", "d", "e", "f", "g"};                                      
PyObject *pFunc, *pArgs, *pValue;
pFunc = td_py_get_callable("my_func");
pArgs = PyTuple_New(2);
pValue = PyLong_FromSize_t((size_t) names);
PyTuple_SetItem(pArgs, 0, pValue);
pValue = PyLong_FromLong(7);
PyTuple_SetItem(pArgs, 1, pValue);
pValue = PyObject_CallObject(pFunc, pArgs);

然后可以使用以下python my_func传递地址和字符串数:

def my_func(names_addr, num_strs):
    type_char_p = ctypes.POINTER(ctypes.c_char_p)
    names = type_char_p.from_address(names_addr)
    for idx in range(num_strs):
        print(names[idx])

当然,谁真的想在Python中传递地址和长度。如果我们需要使用它们,我们可以将它们放在一个numpy数组中并传递然后转换:

def my_func(name_addr, num_strs):
    type_char_p = ctypes.POINTER(ctypes.c_char_p)
    names = type_char_p.from_address(names_addr)
    // Cast to size_t pointers to be held by numpy
    p = ctypes.cast(names, ctypes.POINTER(ctypes.c_size_t))
    name_addrs = numpy.ctypeslib.as_array(p, shape=(num_strs,))
    // pass to some numpy functions
    my_numpy_fun(name_addrs)

挑战在于评估numpy数组的索引只会给你一个地址,但内存与原始c指针相同。我们可以转回ctypes.POINTER(ctypes.c_char_p)来访问值:

def my_numpy_func(name_addrs):
    names = name_addrs.ctypes.data_as(ctypes.POINTER(ctypes.c_char_p))
    for i in range(len(name_addrs)):
        print names[i]

这并不完美,因为我不能使用numpy.searchsorted之类的东西在numpy级别进行二进制搜索,但它确实传递了char *而没有足够的副本。