[英]Passing an array from python to C using ctypes, then using that array in Python
[英]Passing string array from python to C and using in for loop (code included)
我试图将两个字符串数组从 python 传递到 C,在嵌套的 for 循环中使用它们,将它们与一个参数进行比较,如果两个字符串满足这个参数,我将它们附加到 C 中的一个新字符串数组中。该函数以我返回比较字符串的新数组结束。 这个函数是在 python 中使用 CDLL 调用的,这个新的字符串数组是我的 python 脚本。
#In Python:
PyOne = ["Apple", "Orange", "Banana"]
PyTwo = ["Cucumber", "Mango", "Pineapple", "Apple"]
我已将这些翻译用于我的 C 函数,如下所示:
#In Python:
PyOne = ["Apple", "Orange", "Banana"]
PyOne_bytes = []
for i in range(len(PyOne)):
PyOne_bytes.append(bytes(PyOne[i], 'utf-8'))
One_array = (ctypes.c_char_p * (len(PyOne_bytes)+1))()
One_array[:-1] = PyOne_bytes
PyTwo = ["Cucumber", "Mango", "Pineapple", "Apple"]
PyTwo_bytes = []
for i in range(len(PyTwo)):
PyTwo_bytes(bytes(PyTwo[i], 'utf-8'))
Two_array = (ctypes.c_char_p * (len(PyTwo_bytes)+1))()
Two_array[:-1] = PyTwo_bytes
上面的代码将现有的 Python 字符串数组转换为 C 可以解释的字符串数组。
这是我的 C 函数:
// In C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SCALING_FACTOR 0.1
#include <ctype.h>
...
char ** mainForLoop(const char ** PyOne, const char ** PyTwo) {
char ** matches = malloc(100 * sizeof(char*));
size_t i = 0;
size_t j = 0;
for (i = 0; i < sizeof(PyOne)/sizeof(PyOne[0]); i++) {
for (j = 0; j < sizeof(PyTwo)/sizeof(PyTwo[0]); j++) {
double v = comparison(PyOne[i], PyTwo[i]);
if (v > 4) {
strcat(matches, (PyOne[i]));
strcat(matches, (";"));
}
}
}
return matches;
}
在 python 中,我然后打印函数的返回值,如下所示:
c.mainForLoop.argtypes = [POINTER(c_char_p), POINTER(c_char_p)]
c.mainForLoop.restype = ctypes.c_char_p
print(c.mainForLoop(One_array, Two_array))
例如,如果 compare("Apple", "Cucumber") = 5 (ie > 4), compare("Orange", "Mango") = 7 (ie > 4),以及其他所有 compare() < 4,那么我会期望,由于以下原因......
// In C
double v = comparison(PyOne[i], PyTwo[i]);
if (v > 4) {
strcat(matches, (PyOne[i]));
strcat(matches, (";"));
}
为了
#In Python
print(cDoc.mainForLoop(One_array, Two_array))
>>> b'Apple;Orange'
但目前,这会打印:
>>> b'Apple;'
我不知道我的代码做错了什么。 我对 C 有点陌生,我已经尝试了我能想到的一切,任何帮助将不胜感激,解释也将是王牌!
谢谢!
编辑:根据下面的答案,这是我的新代码:
// In C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void free_list(char** list, size_t size) {
for(size_t i = 0; i < size; ++i) if (list[i]) free(list[i]);
free(list);
}
char ** mainForLoop(const char ** PyOne, const char ** PyTwo, size_t sizeOne, size_t sizeTwo) {
size_t i = 0;
size_t j = 0;
char ** matches = malloc(sizeOne * sizeof(char *));
char temp[100] = {0};
for (i = 0; i < sizeOne; i++) {
// Cleared on each pass
temp[0] = 0;
for (j = 0; j < sizeTwo; j++) {
double v = similarity(PyOne[i], PyTwo[j]);
if (v > 4) {
// Works with the temp buffer
strcat(temp, (PyOne[i]));
strcat(temp, (";"));
int size = strlen(temp) + 1; //+1 for null termination
char * str = malloc(size);
memcpy(str, temp, size);
str[size-1] = 0; //Null termination
matches[i] = str;
}
}
}
return matches;
free_list(matches, sizeOne);
}
#In Python
dll = CDLL("c.file")
dll.mainForLoop.argtypes = POINTER(c_char_p),POINTER(c_char_p),c_size_t,c_size_t
dll.mainForLoop.restype = POINTER(c_char_p)
dll.free_list.argtypes = POINTER(c_char_p),c_size_t
dll.free_list.restype = None
def make_clist(lst):
return (c_char_p * len(lst))(*[x.encode() for x in lst])
def mainForLoop(list1,list2):
size = c_size_t()
result = dll.mainForLoop(make_clist(list1),make_clist(list2),len(list1),len(list2))
data = [x.decode() for x in result[:size.value]]
dll.free_list(result,size.value)
return data
list1 = ["Apple", "Orange", "Banana"]
list2 = ["Apple", "Mango", "Pineapple", "Apple"]
print(mainForLoop(list1,list2))
但是,这将返回:
[]
请注意,C 中的“comparison()”函数是一个字符串距离计算,它通过比较两个字符串返回一个双精度值。
任何帮助将不胜感激。
这是一个比您的配置更接近的示例。 请注意,我的main
功能只是用于运行示例,在您的情况下它将毫无用处。
主要的解决方法是我为每个新字符串分配了您没有做的内存(您为指针分配了内存,而不是为字符串本身分配了内存)。 为此,我首先在堆栈上使用一个 100 个字符的临时缓冲区(如果需要,您可以延长它),一旦知道字符串的长度,我就分配它(注意您准备释放这些字符串和指针的函数) (free_list) 已经正确):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
double comparison(const char * one, const char * two) {
int try = rand() % 12;
return try;
}
char ** mainForLoop(char ** PyOne, char ** PyTwo, int sizeOne, int sizeTwo) {
size_t i = 0;
size_t j = 0;
// Allocate an array of N string pointers where N is the size of PyOne
char ** matches = malloc(sizeOne * sizeof(char *));
// The temporary buffer
char temp[100] = {0};
for (i = 0; i < sizeOne; i++) {
// Cleared on each pass
temp[0] = 0;
for (j = 0; j < sizeTwo; j++) {
double v = comparison(PyOne[i], PyTwo[j]);
if (v > 4) {
// Works with the temp buffer
strcat(temp, (PyOne[i]));
strcat(temp, (";"));
int size = strlen(temp) + 1; //+1 for null termination
// Then allocates a string of the right size
char * str = malloc(size);
memcpy(str, temp, size);
str[size-1] = 0; //Null termination
// And collects it
matches[i] = str;
}
}
}
return matches;
}
void free_list(char** list, size_t size) {
for(size_t i = 0; i < size; ++i) if (list[i]) free(list[i]);
free(list);
}
int main() {
// Initializes random
srand(time(0));
int N = 3;
char * PyOne[3] = {"Apple", "Orange", "Banana"};
char * PyTwo[4] = {"Cucumber", "Mango", "Pineapple", "Apple"};
char ** matches = mainForLoop(PyOne, PyTwo, N, 4);
// Prints the result which is possibly (depending on random) :
// "Apple; Orange; Banana;Banana;"
for (char i=0; i<N; i++) printf("%s ", matches[i]);
printf("\n");
// Frees
free_list(matches, N);
return 0;
}
提供的 C 代码不正确,因此这里是演示如何传递和返回字节数组的演示。 此示例只是将两个列表附加在一起。 它还处理释放内存,因此 C 中的内存分配不会导致内存泄漏。
测试.c:
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
#include <stdlib.h>
#include <string.h>
API char** append_lists(const char** list1, size_t size1, const char** list2, size_t size2, size_t* pSize) {
char** total = malloc((size1 + size2) * sizeof(char*));
for(size_t i = 0; i < size1; ++i)
total[i] = _strdup(list1[i]);
for(size_t i = 0; i < size2; ++i)
total[size1 + i] = _strdup(list2[i]);
*pSize = size1 + size2;
return total;
}
API void free_list(char** list, size_t size) {
for(size_t i = 0; i < size; ++i)
free(list[i]);
free(list);
}
测试.py:
from ctypes import *
dll = CDLL('./test')
dll.append_lists.argtypes = POINTER(c_char_p),c_size_t,POINTER(c_char_p),c_size_t,POINTER(c_size_t)
dll.append_lists.restype = POINTER(c_char_p)
dll.free_list.argtypes = POINTER(c_char_p),c_size_t
dll.free_list.restype = None
# Helper function to turn Python list of Unicode strings
# into a ctypes array of byte strings.
def make_clist(lst):
return (c_char_p * len(lst))(*[x.encode() for x in lst])
# Helper function to convert the lists, make the call correctly,
# convert the return result back into a Python list of Unicode strings,
# and free the C allocations.
def append_lists(list1,list2):
size = c_size_t()
result = dll.append_lists(make_clist(list1),len(list1),make_clist(list2),len(list2),byref(size))
data = [x.decode() for x in result[:size.value]]
dll.free_list(result,size.value)
return data
list1 = ["Apple", "Orange", "Banana"]
list2 = ["Cucumber", "Mango", "Pineapple", "Apple"]
print(append_lists(list1,list2))
输出:
['Apple', 'Orange', 'Banana', 'Cucumber', 'Mango', 'Pineapple', 'Apple']
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.