Comment convertir une chaîne C (char array) en une chaîne Python lorsque la chaîne contient des caractères non-ASCII?

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

Question

J'ai intégré un interpréteur Python dans un programme C. Supposons que le programme C lit quelques octets d'un fichier dans un tableau de caractères et apprenne (d'une manière ou d'une autre) que les octets représentent du texte avec un certain codage (par exemple, ISO 8859-1, Windows 1252 ou UTF-8). Comment décoder le contenu de ce tableau de caractères en une chaîne Python?

La chaîne Python doit généralement être du type unicode . Par exemple, un 0x93 dans une entrée codée Windows-1252 devient un 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: le codec 'ascii' ne peut pas coder le caractère u '\ u201c' en position 0: l'ordinal n'est pas dans la plage (128) , ce qui indique que le ascii Le codage est utilisé même si nous spécifions 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 en appelant 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;
}
Était-ce utile?

La solution

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

IOW, il s'agit en gros de ce que vous faites dans votre deuxième exemple: convertir en chaîne, puis décoder la chaîne. Le problème ici provient de PyString_AsDecodedString, plutôt que de 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 à de l'ASCII). C'est là que ça échoue.

Je pense que vous devrez passer deux appels, mais vous pouvez utiliser PyString_AsDecodedObject plutôt que d'appeler le python "decode". méthode. 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 du raisonnement derrière PyString_Decode. Un très ancien fil de discussion sur python-dev semble être indique que cela a quelque chose à voir avec l'enchaînement de la sortie, mais comme les méthodes Python ne font pas la même chose, je ne suis pas sûr que cela soit toujours pertinent.

Autres conseils

Vous ne voulez pas décoder la chaîne en une représentation Unicode, vous voulez simplement la traiter comme un tableau d'octets, n'est-ce pas?

Utilisez simplement PyString_FromString :

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

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

Je ne sais pas trop comment spécifier "str". ou "unicode". Ils sont assez différents si vous avez des caractères non-ASCII. Si vous souhaitez décoder une chaîne de caractères et , vous savez exactement dans quel jeu de caractères se trouve, alors oui, PyString_DecodeString est un bon point de départ.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top