使用 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

不过这会使代码更加复杂。