15.16 不确定编码格式的C字符串

    下面是一些C的数据和一个函数来演示这个问题:

    在这个代码中,字符串 包含了UTF-8和不合格数据。不过,如果用户在C中调用 print_chars(sdata, slen) ,它缺能正常工作。现在假设你想将 的内容转换为一个Python字符串。进一步假设你在后面还想通过一个扩展将那个字符串传个 print_chars() 函数。下面是一种用来保护原始数据的方法,就算它编码有问题。

    1. /* Return the C string back to Python */
    2. static PyObject *py_retstr(PyObject *self, PyObject *args) {
    3. if (!PyArg_ParseTuple(args, "")) {
    4. return NULL;
    5. }
    6. return PyUnicode_Decode(sdata, slen, "utf-8", "surrogateescape");
    7. }
    8.  
    9. /* Wrapper for the print_chars() function */
    10. static PyObject *py_print_chars(PyObject *self, PyObject *args) {
    11. char *s = 0;
    12. Py_ssize_t len;
    13.  
    14. if (!PyArg_ParseTuple(args, "U", &obj)) {
    15. return NULL;
    16. }
    17.  
    18. if ((bytes = PyUnicode_AsEncodedString(obj,"utf-8","surrogateescape"))
    19. == NULL) {
    20. return NULL;
    21. }
    22. PyBytes_AsStringAndSize(bytes, &s, &len);
    23. print_chars(s, len);
    24. Py_RETURN_NONE;
    25. }

    如果你在Python中尝试这些函数,下面是运行效果:

    仔细观察结果你会发现,不合格字符串被编码到一个Python字符串中,并且并没有产生错误,并且当它被回传给C的时候,被转换为和之前原始C字符串一样的字节。

    一般来讲,可以通过制定一些错误策略比如严格、忽略、替代或其他类似的来处理Unicode错误。不过,这些策略的一个缺点是它们永久性破坏了原始字符串的内容。例如,如果例子中的不合格数据使用这些策略之一解码,你会得到下面这样的结果:

    1. >>> raw = b'Spicy Jalape\xc3\xb1o\xae'
    2. >>> raw.decode('utf-8','ignore')
    3. 'Spicy Jalapeño'
    4. >>> raw.decode('utf-8','replace')
    5. 'Spicy Jalapeño?'
    6. >>>

    错误处理策略会将所有不可解码字节转化为一个代理对的低位字节(udcXX中XX是原始字节值)。例如:

    单独的低位代理字符比如 \udcae 在Unicode中是非法的。因此,这个字符串就是一个非法表示。实际上,如果你将它传个一个执行输出的函数,你会得到一个错误:

    1. >>> s = raw.decode('utf-8', 'surrogateescape')
    2. >>> print(s)
    3. Traceback (most recent call last):
    4. File "<stdin>", line 1, in <module>
    5. UnicodeEncodeError: 'utf-8' codec can't encode character '\udcae'
    6. >>>

    然而,允许代理转换的关键点在于从C传给Python又回传给C的不合格字符串不会有任何数据丢失。当这个字符串再次使用 编码时,代理字符会转换回原始字节。例如:

    最后一点要注意的是,Python中许多面向系统的函数,特别是和文件名、环境变量和命令行参数相关的都会使用代理编码。例如,如果你使用像 os.listdir() 这样的函数,传入一个包含了不可解码文件名的目录的话,它会返回一个代理转换后的字符串。参考5.15的相关章节。

    中有更多关于本机提到的以及和surrogateescape错误处理相关的信息。

    原文: