Передача строк с завершающим NULL в библиотеки C
If one wants an extension module that needs to pass a NULL-terminated string to a C library. Let’s see how to do it with Python’s Unicode string implementation. C libraries has many functions that operate on NULL-terminated strings declared as type char *
.
В приведенном ниже коде есть функция C, которую мы проиллюстрируем и проверим. Функция C ( Код # 1 ) просто печатает шестнадцатеричное представление отдельных символов, чтобы переданные строки можно было легко отладить.
Code #1 :
void print_chars( char *s) { while (*s) { printf ( "%2x " , (unsigned char ) *s); s++; } printf ( "
" ); } print_chars( "Hello" ); |
Выход :
48 65 6c 6c 6f
To call such C function from Python, there are few choices. First of it is that – it can be restricted to only operate on bytes using “y” conversion code to PyArg_ParseTuple()
as shown in the code below.
Code #2 :
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.
Code #3 :
print (print_chars(b "Hello World" )) print ( "
" , print_chars(b "Hellox00World" )) print ( "
" , print_chars( "Hello World" )) |
Выход :
48 65 6c 6c 6f 20 57 6f 72 6c 64 Отслеживание (последний вызов последний): Файл "", строка 1, в TypeError: должны быть байты без нулевых байтов, а не байты Отслеживание (последний вызов последний): Файл "", строка 1, в TypeError: 'str' не поддерживает интерфейс буфера
If you want to pass Unicode strings instead, use the “s” format code to PyArg_ParseTuple()
as shown below.
Code #4 :
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; } |
Использование приведенного выше кода ( код # 4 ) автоматически преобразует все строки в кодировку UTF-8 с завершающим NULL. Как показано в приведенном ниже коде.
Код № 5:
print (print_chars( "Hello World" )) # UTF-8 encoding print ( "
" , print_chars( "Spicy Jalapeu00f1o" )) print ( "
" , print_chars( "Hellox00World" )) print ( "
" , print_chars(b "Hello World" )) |
Выход :
48 65 6c 6c 6f 20 57 6f 72 6c 64 53 70 69 63 79 20 4a 61 6c 61 70 65 c3 b1 6f Отслеживание (последний вызов последний): Файл "", строка 1, в TypeError: должно быть str без нулевых символов, а не str Отслеживание (последний вызов последний): Файл "", строка 1, в TypeError: должно быть str, а не байтами
If working with a PyObject *
and can’t use PyArg_ParseTuple()
, the code below explains how to check and extract a suitable char *
reference, from both a bytes and string object.
Code #6 : Conversion from bytes
// Some Python Object PyObject *obj; // Conversion from bytes { char *s; s = PyBytes_AsString(o); if (!s) { /* TypeError already raised */ return NULL; } print_chars(s); } |
Code #7 : 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 где-либо еще внутри строки. Это нужно проверить, если это важно.
Note : There is a hidden memory overhead associated with using the “s” format code to PyArg_ParseTuple()
that is easy to overlook. When writing a code that uses this conversion, a UTF-8 string is created and gets permanently attached to the original string object which if contains non-ASCII characters, makes the size of the string increase until it is garbage collected.
Code #8 :
import sys s = "Spicy Jalapeu00f1o" print ( "Size : " , sys.getsizeof(s)) # passing string print ( "
" , print_chars(s)) # increasing size print ( "
Size : " , sys.getsizeof(s)) |
Выход :
Размер: 87 53 70 69 63 79 20 4a 61 6c 61 70 65 c3 b1 6f Размер: 103
Внимание компьютерщик! Укрепите свои основы с помощью базового курса программирования Python и изучите основы.
Для начала подготовьтесь к собеседованию. Расширьте свои концепции структур данных с помощью курса Python DS. А чтобы начать свое путешествие по машинному обучению, присоединяйтесь к курсу Машинное обучение - базовый уровень.