📜  传递给 C 库的 Unicode 字符串

📅  最后修改于: 2022-05-13 01:54:28.698000             🧑  作者: Mango

传递给 C 库的 Unicode 字符串

假设一个人想要编写一个需要将Python字符串传递给 C 库函数的扩展模块。因此,出现了正确处理 Unicode 的问题。因此,出现的主要问题之一是现有的 C 库无法理解 Python 对 Unicode 的本机表示。因此,主要的挑战是将Python字符串转换为 C 库更容易理解的形式。

为了说明解决方案——下面给出了两个对字符串数据进行操作并将其输出以进行调试和实验的 C 函数。

代码 #1:使用以char *, int形式提供的字节

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


代码 #2:使用wchar_t *, int形式的宽字符

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

对于面向字节的函数print_chars() , Python字符串需要转换为合适的字节编码,例如 UTF-8。下面给出的代码是一个简单的扩展函数,可以解决这个问题。

代码#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;
}

对于与机器原生wchar_t类型一起工作的库函数,C 扩展代码可以写成——

代码#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;
}

现在下面的代码检查扩展功能是如何工作的。

可以观察到面向字节的函数print_chars()接收 UTF-8 编码数据的方式,而print_wchars()接收 Unicode 代码点值。

代码#5:

s = 'Spicy Jalape\u00f1o'
print (print_chars(s))
  
print ("\n", print_wchars(s))

输出 :

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 库,传递 bytes 而不是字符串可能更有意义。让我们使用下面给出的转换代码来做到这一点。

代码#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;
}

如果仍然希望传递字符串,则需要注意 Python3 使用可调整的字符串表示形式,这种表示形式并不完全直接使用标准类型char *wchar_t *直接映射到 C 库。因此,为了将字符串数据呈现给 C,几乎总是需要某种转换。 PyArg_ParseTuple()s#u#格式代码可以安全地执行此类转换。
每当进行转换时,都会将转换后的数据的副本附加到原始字符串对象,以便以后可以重用它,如下面的代码所示。

代码#7:

import sys
  
s = 'Spicy Jalape\u00f1o'
print ("Size : ", sys.getsizeof(s))
  
print("\n", print_chars(s))
  
print ("\nSize : ", sys.getsizeof(s))
  
print ("\n", print_wchars(s))
  
print ("\nSize : ", sys.getsizeof(s))

输出 :

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

Size : 103    

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

Size : 163