Cython - 将字符串列表转换为 char **

8

如何将Python字符串列表转换为以null结尾的char**,以便可以将其传递给外部C函数?

我有以下内容:

struct saferun_task:
    saferun_jail   *jail
    saferun_limits *limits

    char **argv
    int stdin_fd  
    int stdout_fd
    int stderr_fd

int saferun_run(saferun_inst *inst, saferun_task *task, saferun_stat *stat)

在cdef extern块中

我想把像('./a.out','param1','param2')这样的东西转换成可以分配给saferun_task.argv的东西。

如何做到?


1
请查看此链接:https://groups.google.com/forum/?fromgroups#!searchin/cython-users/char**/cython-users/ldtOV1QwITA/bxL1AtiALkwJ - OG Dude
可能是Fast string array - Cython的重复问题。 - Claudiu
2个回答

5

来自Cython文档:

char* PyString_AsString (PyObject *string)

返回字符串内容的以空字符结尾的表示形式。指针引用字符串的内部缓冲区,而不是副本。数据不能以任何方式修改。它不能被释放。

我目前没有设置 Cython 编译器,但这应该会产生类似以下代码的结果:

from libc.stdlib cimport malloc, free

cdef char **string_buf = malloc(len(pystr_list) * sizeof(char*))

for i in range(len(pystr_list)):
    string_buf[i] = PyString_AsString(pystr_list[i])

# Do stuff with string_buf as a char**
# ...

free(string_buf)

指针stringBuf现在是一个char **,指向原始数据而不复制任何字符串 - 尽管你不应该编辑每个字符串中的数据,因为字符串应该被视为const char *(来自文档)。如果你需要操作字符串,你将不得不memcpy数据或创建你不关心在Python中崩溃的新对象 - 虽然由于你有一个字符串元组,我怀疑你是否正在编辑它们。


3
PyString_AsString 只适用于 Python2,因此这种解决方案在 Python3 中不起作用。 - ead

0

Python有权将字符串的内部表示保留在任何非标准格式中。因此,您必须首先将字符串转换为字节,例如使用.encode('utf-8')或任何其他编码格式。

在您有可用的字节之后,您可以通过将字节分配给char *变量来轻松地将它们转换为指针,在Cython代码中只需执行以下操作:

s = 'abc'
b = s.encode('utf-8') + b'\x00'
cdef const char * ptr = b

注意上面的代码中我在字节串后添加了b'\x00',因为字节串的表示形式不必在末尾包含零字节,而C/C++在接受char *字符串时需要该零字节。
同样地,如果C/C++代码返回了char *,那么你可以按照以下方式轻松地将其转换回字符串:
cdef const char * ptr = .... # This pointer is filled-in by C code
b = <bytes>ptr
s = s.decode('utf-8') # Now it contains string

在上面的代码中,注意通过<bytes>ptrchar *转换为字节。Cython通过搜索第一个零字节并截断字符串来将char *强制转换为字节,最终字节不包含零字节。
现在,您还可以创建char **数组以将其传递给C/C ++,如下面的代码所示。 我假设您正在编译64位二进制文件(具有64位指针):
# Imports
import numpy as np
cimport numpy as np
cimport cython
from libc.stdint cimport *

# Cython func
def cython_func():
    ss = ['ab', 'cde', 'f']
    bs = [e.encode('utf-8') + b'\x00' for e in ss]
    a = np.zeros(len(bs), dtype = np.uint64)
    for i in range(len(bs)):
        a[i] = <uint64_t>(<char *>bs[i])

    cdef uint64_t[:] ca = a
    cdef char ** final_ptr = <char **>&ca[0]

    with nogil:
        some_c_func(final_ptr)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接