问题 ctypes和字符串


我有一个简单的C文件:

char* initializetest() {
    char * test = malloc(1000);
    return test;
}

int searchtest( char* test )
{
   strcpy(test,"test");
   return 0;
}

main()
 {
    char *test = initializetest();
    searchtest(test);
    printf("%s\n", test );
}

和python文件:

from ctypes import *

class Test(object):
    def __init__(self):
        self.test_library=CDLL("test.so")
        self.test_initialize = self.test_library.initializetest
        self.test_search = self.test_library.searchtest
        self.test_search.restype=c_int
        self.m = self.test_initialize()

    def search(self):
        self.test_search(self.m)
        print self.m

r = Test()
print r.search()

如何在python中获取“test”字符串?


11906
2017-12-09 14:56


起源



答案:


from ctypes import *

charptr = POINTER(c_char)

test = CDLL('test.so')
test.initializetest.argtypes = []
test.initializetest.restype = charptr
test.searchtest.argtypes = [charptr]
test.searchtest.restype = c_int

buf = test.initializetest()
test.searchtest(buf)
print cast(buf, c_char_p).value
# TODO Release the "buf" memory or it will leak.

编辑

最初我用过 c_char_p 在函数之间传递缓冲区但是 c_char_p 就像一个const指针。如果用作 restype,你真的会得到一个Python str 背部。因此对于 initializetest 它将从分配的内存中创建一个字符串(通过复制数据)并将指针扔掉。

现在我们正在创建一个新类型,a POINTER 至 c_char。然后在两个函数中使用它。

对于Python,这种类型指向一个char,因此我们必须将其强制转换为获取整个字符串 searchtest 已经完成了。我们投了 c_char_p 因为我们只想读取值,所以const指针就可以了。

作为旁注,这说明了使用的灾难性后果 c_char_p 使用修改数组的函数(如 searchtest 以上):

>>> libc.memset.argtypes = [c_char_p, c_int, c_int]
>>> foo = 'Python'
>>> foo
'Python'
>>> libc.memset(foo, ord('x'), 3)
44808532
>>> foo
'xxxhon'
>>>

请注意我们如何设法更改不可变的Python字符串!

argtypes 甚至不需要设置行,因为 ctypes 假设 c_char_p 如果是Python str 用作参数。


11
2017-12-09 15:09



没有 buf = c_char_p(test.initializetest()),它不起作用...... - Cédric Julien
你是对的,它没用。我编辑了答案。请注意,您的修复可能会导致段错误。 - yak
你也是对的,对于演员来说它更好() - Cédric Julien
@yak为什么那段代码会导致段错?我想我正在反对那个案子 - Ciprian Tomoiagă


答案:


from ctypes import *

charptr = POINTER(c_char)

test = CDLL('test.so')
test.initializetest.argtypes = []
test.initializetest.restype = charptr
test.searchtest.argtypes = [charptr]
test.searchtest.restype = c_int

buf = test.initializetest()
test.searchtest(buf)
print cast(buf, c_char_p).value
# TODO Release the "buf" memory or it will leak.

编辑

最初我用过 c_char_p 在函数之间传递缓冲区但是 c_char_p 就像一个const指针。如果用作 restype,你真的会得到一个Python str 背部。因此对于 initializetest 它将从分配的内存中创建一个字符串(通过复制数据)并将指针扔掉。

现在我们正在创建一个新类型,a POINTER 至 c_char。然后在两个函数中使用它。

对于Python,这种类型指向一个char,因此我们必须将其强制转换为获取整个字符串 searchtest 已经完成了。我们投了 c_char_p 因为我们只想读取值,所以const指针就可以了。

作为旁注,这说明了使用的灾难性后果 c_char_p 使用修改数组的函数(如 searchtest 以上):

>>> libc.memset.argtypes = [c_char_p, c_int, c_int]
>>> foo = 'Python'
>>> foo
'Python'
>>> libc.memset(foo, ord('x'), 3)
44808532
>>> foo
'xxxhon'
>>>

请注意我们如何设法更改不可变的Python字符串!

argtypes 甚至不需要设置行,因为 ctypes 假设 c_char_p 如果是Python str 用作参数。


11
2017-12-09 15:09



没有 buf = c_char_p(test.initializetest()),它不起作用...... - Cédric Julien
你是对的,它没用。我编辑了答案。请注意,您的修复可能会导致段错误。 - yak
你也是对的,对于演员来说它更好() - Cédric Julien
@yak为什么那段代码会导致段错?我想我正在反对那个案子 - Ciprian Tomoiagă


也许通过使用类似的restype 这里描述

class Test(object):
    def __init__(self):
        self.test_library=CDLL("./test.so")
        self.test_initialize = self.test_library.initializetest
        self.test_initialize.argtypes = []
        self.test_initialize.restype = c_char_p # c_char_p is a pointer to a string
        self.test_search = self.test_library.searchtest
        self.test_search.restype = c_int
        self.test_search.argtypes = [c_char_p]
        self.m = c_char_p(self.test_initialize())

    def search(self):
        return self.test_search(self.m).value

r = Test()
print r.search()

编辑:测试后纠正:)


1
2017-12-09 15:04





将c_char_p设置为init func的返回类型。 c_char_p表示char *(NUL终止)


0
2017-12-09 15:08