Como converter uma string C (matriz de caracteres) em uma string Python quando há caracteres não ASCII na string?

StackOverflow https://stackoverflow.com/questions/213628

Pergunta

Incorporei um interpretador Python em um programa C.Suponha que o programa C leia alguns bytes de um arquivo em um array char e aprenda (de alguma forma) que os bytes representam texto com uma determinada codificação (por exemplo, ISO 8859-1, Windows-1252 ou UTF-8).Como decodifico o conteúdo deste array char em uma string Python?

A string Python deve, em geral, ser do tipo unicode—por exemplo, um 0x93 na entrada codificada do Windows-1252 torna-se um u'\u0201c'.

Eu tentei usar PyString_Decode, mas sempre falha quando há caracteres não ASCII na string.Aqui está um exemplo que falha:

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

A mensagem de erro é UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128), o que indica que ascii a codificação é usada mesmo que especifiquemos windows_1252 na chamada para PyString_Decode.

O código a seguir contorna o problema usando PyString_FromString para criar uma string Python dos bytes não codificados e, em seguida, chamar seu decode método:

#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;
}
Foi útil?

Solução

Pystring_decode faz isso:

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;
}

Iow, faz basicamente o que você está fazendo no seu segundo exemplo - converte em uma string e decodifique a string. O problema aqui surge de Pystring_asDecodedString, em vez de Pystring_asDecodedObject. Pystring_asDecodedString faz Pystring_asDecodEdObject, mas tenta converter o objeto Unicode resultante em um objeto String com a codificação padrão (para você, parece que isso é ASCII). É aí que falha.

Acredito que você precisará fazer duas chamadas - mas você pode usar o Pystring_asDecodedObject, em vez de chamar o método Python "decodificar". Algo como:

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

Não tenho muita certeza de qual é o raciocínio por trás do Pystring_Decode funcionando dessa maneira. UMA Tópico muito antigo no Python-dev Parece indicar que isso tem algo a ver com o encadeamento da saída, mas como os métodos Python não fazem o mesmo, não tenho certeza se isso ainda é relevante.

Outras dicas

Você não quer decodificar a string em uma representação Unicode, você só quer tratá-la como uma matriz de bytes, certo?

Apenas use PyString_FromString:

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

Isso é tudo.Agora você tem um Python str() objeto.Veja os documentos aqui: https://docs.python.org/2/c-api/string.html

Estou um pouco confuso sobre como especificar "str" ou "unicode". Eles são bem diferentes se você tiver caracteres não-ASCII.Se você deseja decodificar uma string C e você sabe exatamente em que conjunto de caracteres está, então sim, PyString_DecodeString é um bom lugar para começar.

Tente ligar PyErr_Print() no "if (!py_string)"Cláusula. Talvez a exceção do Python forneça mais algumas informações.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top