【问题标题】:Passing a list of strings to from python/ctypes to C function expecting char **将字符串列表从 python/ctypes 传递到期望 char 的 C 函数**
【发布时间】:2011-03-30 12:07:38
【问题描述】:

我有一个 C 函数,它需要一个列表 \0 终止的字符串作为输入:

void external_C( int length , const char ** string_list) { 
   // Inspect the content of string_list - but not modify it.
} 

从python(使用ctypes)我想根据python字符串列表调用这个函数:

def call_c( string_list ):
    lib.external_C( ?? )

call_c( ["String1" , "String2" , "The last string"])

关于如何在 python 端构建数据结构的任何提示?请注意,我保证 C 函数不会更改 string_list 中字符串的内容。

问候

乔金

【问题讨论】:

  • C 函数如何知道它已到达const char * 序列的末尾?
  • 好吧;一般来说,它当然不知道。我的意图是用 NULL 来终止它,或者我可以传入一个长度以及 (char **) 指针——我可以完全控制有问题的 C 库。乔金
  • 如果它不知道,那就没用了。您需要以某种方式告诉它,并告诉我们,以便我们可以为您提供有效的代码。
  • 好的-好的;我不认为那是那么重要。无论如何,我现在已经更改了 C 函数的定义,所以它需要一个长度参数作为第一个参数。
  • 抱歉,external_C 的参数类型应该是什么?

标签: python c ctypes


【解决方案1】:

非常感谢;这就像魅力一样。我还做了一个这样的替代变体:

def call_c( L ):
    arr = (ctypes.c_char_p * (len(L) + 1))()
    arr[:-1] = L
    arr[ len(L) ] = None
    lib.external_C( arr )

然后在 C 函数中,我遍历 (char **) 列表,直到找到 NULL。

【讨论】:

    【解决方案2】:

    使用 ctypes

    创建到期列表(字符串)

    expiries = ["1M", "2M", "3M", "6M","9M", "1Y", "2Y", "3Y","4Y", "5Y", "6Y", "7Y","8Y", "9Y", "10Y", "11Y","12Y", "15Y", "20Y", "25Y", "30Y"]
    

    发送字符串数组的逻辑

    通过在数组中循环将字符串数组转换为字节数组

     expiries_bytes = []
        for i in range(len(expiries)):
            expiries_bytes.append(bytes(expiries[i], 'utf-8'))
    

    逻辑 ctypes 来初始化一个数组长度的指针

    expiries_array = (ctypes.c_char_p * (len(expiries_bytes)+1))()
    

    将字节数组赋值给指针

    expiries_array[:-1] = expiries_bytes
    

    【讨论】:

      【解决方案3】:

      我只是使用 SWIG 类型映射来制作它

      1.在demo.i接口文件中写入自定义的typemap。

      %module demo
      
      /* tell SWIG to treat char ** as a list of strings */
      %typemap(in) char ** {
          // check if is a list
          if(PyList_Check($input))
          {
              int size = PyList_Size($input);
              int i = 0;
              $1 = (char **)malloc((size + 1)*sizeof(char *));
              for(i = 0; i < size; i++)
              {
                  PyObject * o = PyList_GetItem($input, i);
                  if(PyString_Check(o))
                      $1[i] = PyString_AsString(o);
                  else
                  {
                      PyErr_SetString(PyExc_TypeError, "list must contain strings");
                      free($1);
                      return NULL;
                  }
              }
          }
          else
          {
              PyErr_SetString(PyExc_TypeError, "not a list");
              return NULL;
          }
      }
      
      // clean up the char ** array
      %typemap(freearg) char ** {
          free((char *) $1);
      }
      

      2.生成扩展

      $ swig -python demo.i  // generate wrap code
      $ gcc -c -fpic demo.c demo_wrap.c
      $ gcc -shared demo.o demo_wrap.o -o _demo.so
      

      3.在python中导入模块。

      >>> from demo import yourfunction
      

      【讨论】:

        【解决方案4】:
        def call_c(L):
            arr = (ctypes.c_char_p * len(L))()
            arr[:] = L
            lib.external_C(len(L), arr)
        

        【讨论】:

        • 请注意,在 Python 3.x 中,默认情况下字符串是 unicode,因此您应该通过 s.encode('utf-8')bytes(s, 'utf-8')(或您想要使用的任何非 UTF 编码)将字符串转换为字节-8)。
        • 将这个 ^ 合并到答案中会很好
        猜你喜欢
        • 2021-04-27
        • 2017-06-05
        • 2020-08-01
        • 1970-01-01
        • 2020-09-12
        • 1970-01-01
        • 2019-09-28
        • 2017-04-21
        • 2011-01-21
        相关资源
        最近更新 更多