我目前正在与Freeswitch及其event socket library(通过mod event socket)合作。例如:
from ESL import ESLconnection
cmd = 'uuid_kill %s' % active_call # active_call comes from a Django db and is unicode
con = ESLconnection(config.HOST, config.PORT, config.PWD)
if con.connected():
e = con.api(str(cmd))
else:
logging.error('Couldn\'t connect to Freeswitch Mod Event Socket')
正如您所看到的,我必须使用con.api()
明确地转换str()
的参数。如果没有这个,调用将在以下堆栈跟踪中结束:
Traceback (most recent call last):
[...]
e = con.api(cmd)
File "/usr/lib64/python2.7/site-packages/ESL.py", line 87, in api
def api(*args): return apply(_ESL.ESLconnection_api, args)
TypeError: in method 'ESLconnection_api', argument 2 of type 'char const *'
我不明白这个TypeError:这是什么意思? cmd
包含一个字符串,那么当我使用str(cmd)
进行转换时,它是如何解决的?
它可能与通过SWIG生成的Freeswitch的python API有关吗?
答案 0 :(得分:10)
简短回答:cmd
可能包含一个Unicode字符串,该字符串无法轻易转换为const char *
。错误消息可能来自一个包装器框架,它自动为C库编写Python绑定,例如SWIG或ctypes。框架知道如何处理字节字符串,但是在Unicode字符串上发挥作用。传递str(cmd)
会有所帮助,因为它会将Unicode字符串转换为字节字符串,从中可以轻松地提取C代码所期望的const char *
值。
答案很长:
C类型char const *
,通常拼写为const char *
,可以读作“char
的只读数组”,char
是C的拼写方式“字节”。当C函数接受const char *
时,它需要一个“C字符串”,即以{null}字符结尾的char
值数组。方便的是,Python字符串在内部表示为C字符串,带有一些附加信息,例如类型,引用计数和字符串的长度(因此可以使用O(1)复杂度检索字符串长度,并且还可以包含字符串空字符本身)。
Python 2中的Unicode字符串表示为Py_UNICODE
的数组,它们是16位或32位宽,具体取决于操作系统和构建时标志。这样的数组不能传递给需要8位字符数组的代码 - 它需要转换,通常是临时缓冲区,并且必须在不再需要时释放该缓冲区。
例如,C函数strlen
的一个简单(并且非常不必要)的包装器可能如下所示:
PyObject *strlen(PyObject *ignore, PyObject *obj)
{
const char *c_string;
size_t len;
if (!PyString_Check(obj)) {
PyErr_Format(PyExc_TypeError, "string expected, got %s", Py_TYPE(obj)->tp_name);
return NULL;
}
c_string = PyString_AsString(obj);
len = strlen(c_string);
return PyInt_FromLong((long) len);
}
代码只需调用PyString_AsString
来检索由strlen
所需的每个Python字符串存储的内部C字符串。对于此代码也支持Unicode对象(假设它甚至在Unicode对象上调用strlen
),它必须明确处理它们:
PyObject *strlen(PyObject *ignore, PyObject *obj)
{
const char *c_string;
size_t len;
PyObject *tmp = NULL;
if (PyString_Check(obj))
c_string = PyString_AsString(obj);
else if (PyUnicode_Check(obj)) {
if (!(tmp = PyUnicode_AsUTF8String(obj)))
return NULL;
c_string = PyString_AsString(tmp);
}
else {
PyErr_Format(PyExc_TypeError, "string or unicode expected, got %s",
Py_TYPE(obj)->tp_name);
return NULL;
}
len = strlen(c_string);
Py_XDECREF(tmp);
return PyInt_FromLong((long) len);
}
注意额外的复杂性,不仅在样板代码行中,而且在需要对保存Unicode字符串的字节表示的临时对象进行不同管理的不同代码路径中。另请注意,在将Unicode字符串转换为字节字符串时,需要决定使用编码。 UTF-8保证能够编码任何Unicode字符串,但是将UTF-8编码的序列传递给期望C字符串的函数可能对某些用途没有意义。 str
函数使用ASCII编解码器对Unicode字符串进行编码,因此如果Unicode字符串实际上包含任何非ASCII字符,则会出现异常。
已经有requests to include this functionality in SWIG,但链接报告中是否有明确说明它是否已经显示。
答案 1 :(得分:2)
我有类似的问题,我通过这样做解决了这个问题:
cmd = 'uuid_kill %s'.encode('utf-8')