传递NULL结尾的字符串给C函数库
许多C函数库包含一些操作NULL结尾的字符串,被声明类型为 char * . 考虑如下的C函数,我们用来做演示和测试用的:
void print_chars(char *s) {
while (*s) {
printf('%2x ', (unsigned char) *s);
s++;
}
printf('\n');
}
此函数会打印被传进来字符串的每个字符的十六进制表示,这样的话可以很容易的进行调试了。例如:
print_chars('Hello'); // Outputs: 48 65 6c 6c 6f
对于在Python中调用这样的C函数,你有几种选择。 首先,你可以通过调用 PyArg_ParseTuple() 并指定”y“转换码来限制它只能操作字节,如下:
static PyObject *py_print_chars(PyObject *self, PyObject *args) {
char *s;
if (!PyArg_ParseTuple(args, 'y', &s)) {
return NULL;
}
print_chars(s);
Py_RETURN_NONE;
}
结果函数的使用方法如下。仔细观察嵌入了NULL字节的字符串以及Unicode支持是怎样被拒绝的:
>>> print_chars(b'Hello World')
48 65 6c 6c 6f 20 57 6f 72 6c 64
>>> print_chars(b'Hello\x00World')
Traceback (most recent call last):
File '', line 1, in
TypeError: must be bytes without null bytes, not bytes
>>> print_chars('Hello World')
Traceback (most recent call last):
File '', line 1, in
TypeError: 'str' does not support the buffer interface
>>>
如果你想传递Unicode字符串,在 PyArg_ParseTuple() 中使用”s“格式码,如下:
static PyObject *py_print_chars(PyObject *self, PyObject *args) {
char *s;
if (!PyArg_ParseTuple(args, 's', &s)) {
return NULL;
}
print_chars(s);
Py_RETURN_NONE;
}
当被使用的时候,它会自动将所有字符串转换为以NULL结尾的UTF-8编码。例如:
>>> print_chars('Hello World')
48 65 6c 6c 6f 20 57 6f 72 6c 64
>>> print_chars('Spicy Jalape\u00f1o') # Note: UTF-8 encoding
53 70 69 63 79 20 4a 61 6c 61 70 65 c3 b1 6f
>>> print_chars('Hello\x00World')
Traceback (most recent call last):
File '', line 1, in
TypeError: must be str without null characters, not str
>>> print_chars(b'Hello World')
Traceback (most recent call last):
File '', line 1, in
TypeError: must be str, not bytes
>>>
如果因为某些原因,你要直接使用 PyObject * 而不能使用 PyArg_ParseTuple() , 下面的例子向你展示了怎样从字节和字符串对象中检查和提取一个合适的 char * 引用:
/* Some Python Object (obtained somehow) */
PyObject *obj;
/* Conversion from bytes */
{
char *s;
s = PyBytes_AsString(o);
if (!s) {
return NULL; /* TypeError already raised */
}
print_chars(s);
}
/* Conversion to UTF-8 bytes from a string */
{
PyObject *bytes;
char *s;
if (!PyUnicode_Check(obj)) {
PyErr_SetString(PyExc_TypeError, 'Expected string');
return NULL;
}
bytes = PyUnicode_AsUTF8String(obj);
s = PyBytes_AsString(bytes);
print_chars(s);
Py_DECREF(bytes);
}
前面两种转换都可以确保是NULL结尾的数据, 但是它们并不检查字符串中间是否嵌入了NULL字节。 因此,如果这个很重要的话,那你需要自己去做检查了。
讨论
赞0
踩0