简体   繁体   English

将字符串数组从 python 传递到 C 并在 for 循环中使用(包括代码)

[英]Passing string array from python to C and using in for loop (code included)

I am trying to pass in two string arrays from python into C, using them in a nested for loop, comparing them against a parameter, and if the two strings meet this parameter, I append them into a new string array in C. The function ends with me returning the new array of compared strings.我试图将两个字符串数组从 python 传递到 C,在嵌套的 for 循环中使用它们,将它们与一个参数进行比较,如果两个字符串满足这个参数,我将它们附加到 C 中的一个新字符串数组中。该函数以我返回比较字符串的新数组结束。 This function is called in python using CDLL, and this new string array is used is my python script.这个函数是在 python 中使用 CDLL 调用的,这个新的字符串数组是我的 python 脚本。

#In Python:

PyOne = ["Apple", "Orange", "Banana"]
PyTwo = ["Cucumber", "Mango", "Pineapple", "Apple"]

I have translated these for use in my C function as follows:我已将这些翻译用于我的 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

The above code translates the existing Python string array into one that is interpretable by C.上面的代码将现有的 Python 字符串数组转换为 C 可以解释的字符串数组。

This is my C function:这是我的 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;
}

In python, I then print the returned value from the function as follows:在 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))

If, for example, comparison("Apple", "Cucumber") = 5 (ie > 4), comparison("Orange", "Mango") = 7 (ie > 4), and everything other comparison() < 4, then I would expect, due to the following...例如,如果 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, (";"));
                }

for为了

#In Python
print(cDoc.mainForLoop(One_array, Two_array))

>>> b'Apple;Orange'

but currently, this prints:但目前,这会打印:

>>> b'Apple;'

I don't know what I'm doing wrong in my code.我不知道我的代码做错了什么。 I'm somewhat new to C, and I've tried everything I can think of, any help would be appreciated, an explanation would also be ace!我对 C 有点陌生,我已经尝试了我能想到的一切,任何帮助将不胜感激,解释也将是王牌!

Thank you!谢谢!

EDIT: Following on the from the answers below, this is my new code:编辑:根据下面的答案,这是我的新代码:

// 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))

However, this returns:但是,这将返回:

[] []

Please note that the "comparison()" function in C is a string distance calculation that returns a double value by comparing two strings.请注意,C 中的“comparison()”函数是一个字符串距离计算,它通过比较两个字符串返回一个双精度值。

Any help would be hugely appreciated.任何帮助将不胜感激。

Here is a example closer than your configuration.这是一个比您的配置更接近的示例。 Note that my main function is just there to run the example, in you case it would be useless.请注意,我的main功能只是用于运行示例,在您的情况下它将毫无用处。

The main fix is that I allocate memory for each new string what you didn't do (you allocated the memory for pointers but not for the strings themselves).主要的解决方法是我为每个新字符串分配了您没有做的内存(您为指针分配了内存,而不是为字符串本身分配了内存)。 To do that I first use a 100 characters temporary buffer on the stack (you can make it longer if needed) and, once the length of the string is known, I allocate it (note that the function you prepared for freeing these strings and pointers (free_list) is already correct) :为此,我首先在堆栈上使用一个 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; 
}

The C code provided is incorrect, so here is demo showing how to pass and return byte arrays.提供的 C 代码不正确,因此这里是演示如何传递和返回字节数组的演示。 This example just appends the two lists together.此示例只是将两个列表附加在一起。 It also handles freeing the memory so there is no memory leak from the memory allocations in C.它还处理释放内存,因此 C 中的内存分配不会导致内存泄漏。

test.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);
}

test.py:测试.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))

Output:输出:

['Apple', 'Orange', 'Banana', 'Cucumber', 'Mango', 'Pineapple', 'Apple']

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM