¿Cómo convertir una cadena C (matriz de caracteres) en una cadena Python cuando hay caracteres que no son ASCII en la cadena?

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

Pregunta

He incorporado un intérprete de Python en un programa en C. Supongamos que el programa C lee algunos bytes de un archivo en una matriz de caracteres y aprende (de alguna manera) que los bytes representan texto con cierta codificación (por ejemplo, ISO 8859-1, Windows-1252 o UTF-8). ¿Cómo decodifico el contenido de esta matriz de caracteres en una cadena de Python?

La cadena de Python en general debe ser del tipo unicode & # 8212; por ejemplo, un 0x93 en la entrada codificada de Windows-1252 se convierte en un u '\ u0201c '.

He intentado usar PyString_Decode , pero siempre falla cuando hay caracteres que no son ASCII en la cadena. Aquí hay un ejemplo que falla:

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

El mensaje de error es UnicodeEncodeError: el códec 'ascii' no puede codificar el carácter u '\ u201c' en la posición 0: el ordinal no está en el rango (128) , lo que indica que el ascii La codificación se utiliza aunque especificamos windows_1252 en la llamada a PyString_Decode .

El siguiente código resuelve el problema usando PyString_FromString para crear una cadena Python de los bytes no decodificados, y luego llama a su método de 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;
}
¿Fue útil?

Solución

PyString_Decode hace esto:

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, básicamente hace lo que estás haciendo en tu segundo ejemplo: se convierte en una cadena y luego decodifica la cadena. El problema aquí surge de PyString_AsDecodedString, en lugar de PyString_AsDecodedObject. PyString_AsDecodedString hace PyString_AsDecodedObject, pero luego intenta convertir el objeto Unicode resultante en un objeto de cadena con la codificación predeterminada (para usted, parece que eso es ASCII). Ahí es donde falla.

Creo que necesitarás hacer dos llamadas, pero puedes usar PyString_AsDecodedObject en lugar de llamar a Python " decode " método. 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;
}

No estoy completamente seguro de cuál es el razonamiento detrás de PyString_Decode que funciona de esta manera. Un hilo muy antiguo en python-dev parece indica que tiene algo que ver con el encadenamiento de la salida, pero como los métodos de Python no hacen lo mismo, no estoy seguro de que eso sea relevante.

Otros consejos

No quieres decodificar la cadena en una representación Unicode, solo quieres tratarla como una matriz de bytes, ¿no?

Simplemente use PyString_FromString :

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

Eso es todo. Ahora tienes un objeto str () de Python. Vea los documentos aquí: https://docs.python.org/2/c- api / string.html

Estoy un poco confundido acerca de cómo especificar " str " o " Unicode. " Son muy diferentes si tienes caracteres que no son ASCII. Si desea descodificar una cadena C y sabe exactamente en qué conjunto de caracteres está, entonces sí, PyString_DecodeString es un buen lugar para comenzar.

Intente llamar PyErr_Print () en el " if (! py_string) " cláusula. Tal vez la excepción de Python le dará más información.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top