使用 Cython 将 C 中的字符串数组转换为 Python 列表的规范方法
Canonical way to convert an array of strings in C to a Python list using Cython
我正在使用 Cython 将 C 库与 Python 连接起来。库函数 returns 类型为 char**
的空终止字符串数组,我想将其转换为 str
的 Python list
。下面的代码可以工作,但它看起来脆弱和笨拙,我想知道是否有更简单的方法来做到这一点:
# myfile.pyx
from cython.operator import dereference
def results_from_c():
cdef char** cstringsptr = my_c_function()
strings = []
string = dereference(cstringsptr)
while string != NULL:
strings.append(string.decode())
cstringsptr += 1
string = dereference(cstringsptr)
return strings
特别是,是否可以像在 C 中那样使用 cstringsptr += 1
获取数组中的下一个字符串,例如cstringsptr++;
?这通常是将数组转换为列表的可靠方法吗?如果例如内存分配失败或字符串不是空终止的并且它永远循环?在我看来,使用 Cython 应该有一种更简单的方法来做到这一点。
如果您使用有效的 C 数据结构,字符串 将 为 null-terminated。问题是,字符串指针数组是如何终止的?库(或 my_c_function()
)确保在最后一个字符串指针之后有一个 NULL,或者它使数组长度以其他方式可用。确保你知道它是哪个,并且不要让你的循环终止于空指针,除非你保证会有一个。
为了完成@alexis 的回答,在性能方面,使用append
非常慢(因为它在内部使用了一个不断增长的数组)并且它可以被直接索引取代。这个想法是执行两次步行以了解字符串的数量。虽然走两步看起来很昂贵,但事实并非如此,因为编译器应该优化此循环。如果代码是用最高优化级别 (-O3
) 编译的,第一个循环应该使用非常快的 SIMD 指令。一旦知道长度,列表就可以 allocated/filled 以更快的方式。字符串解码应该占用大部分时间。默认使用 UTF-8 解码。这有点昂贵,假设已知字符串不包含特殊字符,使用 ASCII 解码应该会更快一些。
这是一个未经测试的代码示例:
from cython.operator import dereference
def results_from_c():
cdef char** cstringsptr = my_c_function()
cdef int length = 0
cdef int i
string = dereference(cstringsptr)
while string != NULL:
cstringsptr += 1
length += 1
string = dereference(cstringsptr)
cstringsptr -= length
# None is just a null pointer so that this just allocates a 0-filled array
strings = [None] * length
for i in range(length):
string = dereference(cstringsptr + i)
strings[i] = string.decode()
return strings
不过这会使代码更加复杂。
我正在使用 Cython 将 C 库与 Python 连接起来。库函数 returns 类型为 char**
的空终止字符串数组,我想将其转换为 str
的 Python list
。下面的代码可以工作,但它看起来脆弱和笨拙,我想知道是否有更简单的方法来做到这一点:
# myfile.pyx
from cython.operator import dereference
def results_from_c():
cdef char** cstringsptr = my_c_function()
strings = []
string = dereference(cstringsptr)
while string != NULL:
strings.append(string.decode())
cstringsptr += 1
string = dereference(cstringsptr)
return strings
特别是,是否可以像在 C 中那样使用 cstringsptr += 1
获取数组中的下一个字符串,例如cstringsptr++;
?这通常是将数组转换为列表的可靠方法吗?如果例如内存分配失败或字符串不是空终止的并且它永远循环?在我看来,使用 Cython 应该有一种更简单的方法来做到这一点。
如果您使用有效的 C 数据结构,字符串 将 为 null-terminated。问题是,字符串指针数组是如何终止的?库(或 my_c_function()
)确保在最后一个字符串指针之后有一个 NULL,或者它使数组长度以其他方式可用。确保你知道它是哪个,并且不要让你的循环终止于空指针,除非你保证会有一个。
为了完成@alexis 的回答,在性能方面,使用append
非常慢(因为它在内部使用了一个不断增长的数组)并且它可以被直接索引取代。这个想法是执行两次步行以了解字符串的数量。虽然走两步看起来很昂贵,但事实并非如此,因为编译器应该优化此循环。如果代码是用最高优化级别 (-O3
) 编译的,第一个循环应该使用非常快的 SIMD 指令。一旦知道长度,列表就可以 allocated/filled 以更快的方式。字符串解码应该占用大部分时间。默认使用 UTF-8 解码。这有点昂贵,假设已知字符串不包含特殊字符,使用 ASCII 解码应该会更快一些。
这是一个未经测试的代码示例:
from cython.operator import dereference
def results_from_c():
cdef char** cstringsptr = my_c_function()
cdef int length = 0
cdef int i
string = dereference(cstringsptr)
while string != NULL:
cstringsptr += 1
length += 1
string = dereference(cstringsptr)
cstringsptr -= length
# None is just a null pointer so that this just allocates a 0-filled array
strings = [None] * length
for i in range(length):
string = dereference(cstringsptr + i)
strings[i] = string.decode()
return strings
不过这会使代码更加复杂。