Строки Unicode, передаваемые в библиотеки C

Опубликовано: 12 Апреля, 2022

Учитывая, что кто-то хочет написать модуль расширения, который должен передавать строку Python в библиотечную функцию C. Итак, возникает вопрос, как правильно обрабатывать Unicode. Итак, одна из основных возникающих проблем заключается в том, что существующие библиотеки C не понимают собственного представления Unicode в Python. Следовательно, основная задача - преобразовать строку Python в форму, более понятную для библиотек C.

Чтобы проиллюстрировать решение - ниже приведены две функции C, которые работают со строковыми данными и выводят их для отладки и экспериментов.

Code #1 : Uses bytes provided in the form char *, int

void print_chars(char *s, int len)
{
    int n = 0;
    while (n < len)
    {
        printf("%2x ", (unsigned char) s[n]);
        n++;
    }
    printf(" ");
}

 
Code #2 : Uses wide characters in the form wchar_t *, int

void print_wchars(wchar_t *s, int len)
{
    int n = 0;
    while (n < len)
    {
        printf("%x ", s[n]);
        n++;
    }
    printf(" ");
}

Python strings need to be converted to a suitable byte encoding such as UTF-8 for the byte-oriented function print_chars(). The code given below a simple extension function solving the purpose.

Code #3 :

static PyObject *py_print_chars(PyObject *self, PyObject *args)
{
    char *s;
    Py_ssize_t len;
    if (!PyArg_ParseTuple(args, "s#", &s, &len))
    {
        return NULL;
    }
    print_chars(s, len);
    Py_RETURN_NONE;
}

For library functions that work with the machine native wchar_t type, C extension code can be written as –

Code #4 :

static PyObject * py_print_wchars(PyObject * self, PyObject * args)
{
    wchar_t * s;
    Py_ssize_t len;
    if (! PyArg_ParseTuple(args, "u#", &s, &len))
    {
        return NULL;
    }
    print_wchars(s, len);
    Py_RETURN_NONE;
}

Теперь приведенный ниже код проверяет, как работают функции расширения.

It is to be observed the way the byte-oriented function print_chars() is receiving UTF-8 encoded data, whereas print_wchars() is receiving the Unicode code point values.

Code #5 :

s = "Spicy Jalapeu00f1o"
print (print_chars(s))
  
print (" ", print_wchars(s))

Output :

53 70 69 63 79 20 4a 61 6c 61 70 65 c3 b1 6f

53 70 69 63 79 20 4a 61 6c 61 70 65 f1 6f

Давайте проверим природу библиотеки C. Для многих библиотек C может иметь смысл передавать байты вместо строки. Для этого воспользуемся приведенным ниже кодом преобразования.

Code #6 :

static PyObject *py_print_chars(PyObject *self, PyObject *args)
{
    char *s;
    Py_ssize_t len;
      
    // accepts bytes, bytearray, or other byte-like object 
      
    if (!PyArg_ParseTuple(args, "y#", &s, &len))
    {
        return NULL;
    }
    print_chars(s, len);
    Py_RETURN_NONE;
}

If still desire to pass strings, it is to be taken care that Python3 uses an adaptable string representation that is not entirely straightforward to map directly to C libraries using the standard types char * or wchar_t *. Thus, in order to present string data to C, some kind of conversion is almost always necessary. The s# and u# format codes to PyArg_ParseTuple() safely perform such conversions.
Whenever a conversion is made, a copy of the converted data is attached to the original string object so that it can be reused later as shown in the code below.

Code #7 :

import sys
  
s = "Spicy Jalapeu00f1o"
print ("Size : ", sys.getsizeof(s))
  
print(" ", print_chars(s))
  
print (" Size : ", sys.getsizeof(s))
  
print (" ", print_wchars(s))
  
print (" Size : ", sys.getsizeof(s))

Выход :

Размер: 87
    
53 70 69 63 79 20 4a 61 6c 61 70 65 c3 b1 6f

Размер: 103    

53 70 69 63 79 20 4a 61 6c 61 70 65 f1 6f

Размер: 163    

Внимание компьютерщик! Укрепите свои основы с помощью базового курса программирования Python и изучите основы.

Для начала подготовьтесь к собеседованию. Расширьте свои концепции структур данных с помощью курса Python DS. А чтобы начать свое путешествие по машинному обучению, присоединяйтесь к курсу Машинное обучение - базовый уровень.