2008-10-17 12 views
7

He incorporado un intérprete de Python en un programa C. Supongamos que el programa C lee algunos bytes de un archivo en una matriz char 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 los contenidos de esta matriz char en una cadena de Python?Cómo convertir una cadena C (matriz char) en una cadena Python cuando hay caracteres no ASCII en la cadena?

La cadena de Python debería ser, en general, del tipo unicode -por ejemplo, una entrada codificada con 0x93 en Windows-1252 pasa a ser u'\u0201c'.

He intentado utilizar 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: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128), lo que indica que la codificación ascii se utiliza a pesar de que especificamos windows_1252 en la llamada a PyString_Decode.

El código siguiente funciona alrededor del problema utilizando PyString_FromString para crear una cadena de Python de los bytes no decodificados, a continuación, llamando a su método 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; 
} 
+0

Para elegir nit, una cadena en C es un char [], no un char * –

+1

Para elegir nit, al hacer referencia a un valor, no importa. Las matrices se pasan como punteros a las funciones, de todos modos. – gnud

Respuesta

6

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

OIA, lo hace básicamente lo que está haciendo en su segundo ejemplo - se convierte en una cadena, entonces decodificar 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 es ASCII). Ahí es donde falla.

Creo que tendrá que hacer dos llamadas, pero puede usar PyString_AsDecodedObject en lugar de llamar al método de python "decode". Algo así 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 del todo seguro de lo que el razonamiento detrás de PyString_Decode trabajar de esta manera es. Un very old thread on python-dev parece indicar que tiene algo que ver con encadenar la salida, pero dado que los métodos de Python no hacen lo mismo, no estoy seguro de si eso sigue siendo relevante.

+0

Opps! Gracias Ljosa; fijo. –

+0

Para Python3 https://docs.python.org/3.5/c-api/unicode.html#c.PyUnicode_FromString – crizCraig

3

Usted no quiere para decodificar la cadena en una representación Unicode , solo quieres tratarlo como una matriz de bytes, ¿verdad?

Sólo tiene que utilizar PyString_FromString:

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

eso es todo. Ahora tiene un objeto Python str(). Ver documentos aquí: https://docs.python.org/2/c-api/string.html

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

+0

Quiero decodificarlo, así que cualquier código de Python que termine usando la cadena no necesita saber cómo se codificó originalmente (en la entrada al programa C). Gracias por señalar que no estaba claro; He editado mi pregunta. –

2

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

+0

Gracias, lo hice e incorporé la información en la pregunta. –

+0

No hay problema. Si el consejo fue útil, agradecería un voto positivo. :-) –

Cuestiones relacionadas