Как преобразовать строку C (массив символов) в строку Python, если в строке есть не-ASCII-символы? - PullRequest
7 голосов
/ 17 октября 2008

Я встроил интерпретатор Python в C-программу. Предположим, что программа C считывает некоторые байты из файла в массив char и узнает (каким-то образом), что байты представляют текст с определенной кодировкой (например, ISO 8859-1, Windows-1252 или UTF-8). Как мне декодировать содержимое этого массива char в строку Python?

Строка Python обычно должна иметь тип unicode - например, 0x93 в кодированном входе Windows-1252 становится 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), которое указывает, что используется кодировка ascii, хотя мы указываем windows_1252 в вызове PyString_Decode.

Следующий код решает эту проблему, используя 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 ]

6 голосов
/ 18 октября 2008

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, но затем пытается преобразовать полученный объект Unicode в строковый объект с кодировкой по умолчанию (для вас это выглядит как ASCII) Вот где это терпит неудачу.

Я считаю, что вам нужно будет сделать два вызова - но вы можете использовать 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 не делают то же самое, я не уверен, что это все еще актуально.

3 голосов
/ 18 октября 2008

Вы не хотите декодировать строку в представление 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 - хорошее место для начала.

2 голосов
/ 18 октября 2008

Попробуйте позвонить PyErr_Print() в предложении "if (!py_string)". Возможно, исключение Python даст вам больше информации.

...