2008-10-17 7 views
7

J'ai intégré un interpréteur Python dans un programme C. Supposons que le programme C lit certains octets d'un fichier dans un tableau char et apprenne (en quelque sorte) que les octets représentent du texte avec un certain codage (par exemple, ISO 8859-1, Windows-1252 ou UTF-8). Comment puis-je décoder le contenu de ce tableau char dans une chaîne Python?Comment convertir une chaîne C (tableau char) en une chaîne Python quand il y a des caractères non-ASCII dans la chaîne?

La chaîne Python doit généralement être de type unicode (par exemple, une entrée codée 0x93 dans Windows-1252 devient u'\u0201c').

J'ai essayé d'utiliser PyString_Decode, mais il échoue toujours lorsqu'il y a des caractères non-ASCII dans la chaîne. Voici un exemple qui échoue:

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

Le message d'erreur est UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128), ce qui indique que le codage est utilisé ascii même si nous précisons windows_1252 dans l'appel à PyString_Decode.

Le code suivant contourne le problème en utilisant PyString_FromString pour créer une chaîne Python des octets non décodés, puis appeler sa méthode 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

Pour sélectionner nit, une chaîne C est un char [], pas un char * –

+1

Pour sélectionner nit, lorsque vous référencez une valeur, cela n'a pas d'importance. Les tableaux sont passés comme des pointeurs sur les fonctions, de toute façon. – gnud

Répondre

6

PyString_Decode fait ceci:

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

OIEau, il fait essentiellement ce que vous faites dans votre deuxième exemple - se transforme en une chaîne, puis décode la chaîne. Le problème ici provient de PyString_AsDecodedString, plutôt que PyString_AsDecodedObject. PyString_AsDecodedString fait PyString_AsDecodedObject, mais essaie ensuite de convertir l'objet unicode résultant en un objet chaîne avec l'encodage par défaut (pour vous, cela ressemble à ASCII). C'est là où ça échoue.

Je crois que vous aurez besoin de faire deux appels - mais vous pouvez utiliser PyString_AsDecodedObject plutôt que d'appeler la méthode "decode" python. Quelque chose comme:

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

Je ne suis pas tout à fait sûr de ce que le raisonnement derrière PyString_Decode travailler de cette façon est. Un very old thread on python-dev semble indiquer qu'il a quelque chose à voir avec le chaînage de la sortie, mais puisque les méthodes Python ne font pas la même chose, je ne suis pas sûr que cela soit toujours pertinent.

+0

Opps! Merci Ljosa; fixé. –

+0

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

3

Vous ne voulez pas décoder la chaîne en une représentation Unicode , vous voulez juste le traiter comme un tableau d'octets, non?

Il suffit d'utiliser PyString_FromString:

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

C'est tout. Vous avez maintenant un objet Python str(). Voir les documents ici: https://docs.python.org/2/c-api/string.html

Je suis un peu confus sur la façon de spécifier "str" ​​ou "unicode". Ils sont assez différents si vous avez des caractères non-ASCII. Si vous voulez décoder une chaîne C et vous savez exactement dans quel jeu de caractères il est, alors oui, PyString_DecodeString est un bon point de départ.

+0

Je veux vraiment le décoder, donc quel que soit le code Python finissant par utiliser la chaîne n'a pas besoin de savoir comment il a été codé à l'origine (dans l'entrée du programme C). Merci d'avoir souligné que je n'étais pas clair; J'ai édité ma question. –

2

Essayez d'appeler PyErr_Print() dans la clause "if (!py_string)". Peut-être que l'exception python vous donnera plus d'informations.

+0

Merci, j'ai fait et incorporé l'information dans la question. –

+0

Pas de problème. Si le conseil était utile, j'apprécierais un upvote. :-) –

Questions connexes