如何将包含非ASCII字符的C字符串(字符数组)转换为Python字符串?

7 投票
3 回答
14869 浏览
提问于 2025-04-11 09:28

我在一个C程序里嵌入了Python解释器。假设这个C程序从一个文件中读取了一些字节到一个字符数组里,并且以某种方式得知这些字节代表的是某种编码的文本(比如ISO 8859-1、Windows-1252或者UTF-8)。那么,我该如何把这个字符数组的内容解码成Python字符串呢?

一般来说,Python字符串应该是unicode类型的——比如,在Windows-1252编码的输入中,0x93会变成u'\u0201c'

我尝试使用PyString_Decode,但当字符串中有非ASCII字符时,它总是失败。这里有一个失败的例子:

#include <Python.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
     char c_string[] = { (char)0x93, 0 };
     PyObject *py_string;

     Py_Initialize();

     py_string = PyString_Decode(c_string, 1, "windows_1252", "replace");
     if (!py_string) {
          PyErr_Print();
          return 1;
     }
     return 0;
}

错误信息是UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128),这表明即使我们在调用PyString_Decode时指定了windows_1252,它仍然使用了ascii编码。

下面的代码通过使用PyString_FromString来创建一个未解码字节的Python字符串,然后调用它的decode方法,解决了这个问题:

#include <Python.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
     char c_string[] = { (char)0x93, 0 };
     PyObject *raw, *decoded;

     Py_Initialize();

     raw = PyString_FromString(c_string);
     printf("Undecoded: ");
     PyObject_Print(raw, stdout, 0);
     printf("\n");
     decoded = PyObject_CallMethod(raw, "decode", "s", "windows_1252");
     Py_DECREF(raw);
     printf("Decoded: ");
     PyObject_Print(decoded, stdout, 0);
     printf("\n");
     return 0;
}

3 个回答

2

试着在 "if (!py_string)" 这个条件里调用一下 PyErr_Print()。这样做可能会让你看到一些关于 Python 错误的更多信息。

3

你不想把这个字符串解码成Unicode格式,你只是想把它当成字节数组来处理,对吧?

那就直接使用 PyString_FromString 就可以了:

char *cstring;
PyObject *pystring = PyString_FromString(cstring);

就这么简单。现在你有了一个Python的 str() 对象。想了解更多,可以查看这里的文档: https://docs.python.org/2/c-api/string.html

我有点困惑,关于如何指定“str”或“unicode”。如果你有非ASCII字符,它们之间的区别就很大。如果你想解码一个C字符串,并且你确切知道它使用的字符集,那么是的,PyString_DecodeString 是个不错的起点。

6

PyString_Decode 的作用是这样的:

PyObject *PyString_Decode(const char *s,
              Py_ssize_t size,
              const char *encoding,
              const char *errors)
{
    PyObject *v, *str;

    str = PyString_FromStringAndSize(s, size);
    if (str == NULL)
    return NULL;
    v = PyString_AsDecodedString(str, encoding, errors);
    Py_DECREF(str);
    return v;
}

换句话说,它基本上就是你在第二个例子中做的事情——先转换成字符串,然后再解码这个字符串。这里的问题出在 PyString_AsDecodedString,而不是 PyString_AsDecodedObject。PyString_AsDecodedString 调用了 PyString_AsDecodedObject,但之后又试图用默认编码(对你来说,看起来是 ASCII)把得到的 Unicode 对象转换成字符串对象。这就是它失败的原因。

我认为你需要调用两次——但你可以使用 PyString_AsDecodedObject,而不是调用 Python 的 "decode" 方法。可以这样做:

#include <Python.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
     char c_string[] = { (char)0x93, 0 };
     PyObject *py_string, *py_unicode;

     Py_Initialize();

     py_string = PyString_FromStringAndSize(c_string, 1);
     if (!py_string) {
          PyErr_Print();
          return 1;
     }
     py_unicode = PyString_AsDecodedObject(py_string, "windows_1252", "replace");
     Py_DECREF(py_string);

     return 0;
}

我不太确定 PyString_Decode 为什么要这样工作的原因。一个很久以前的 python-dev 讨论似乎表明这和输出的链式处理有关,但由于 Python 的方法并没有这样做,我不确定这是否仍然适用。

撰写回答