Come convertire una stringa C (array di caratteri) in una stringa Python quando nella stringa sono presenti caratteri non ASCII?
-
03-07-2019 - |
Domanda
Ho incorporato un interprete Python in un programma C. Supponiamo che il programma C legga alcuni byte da un file in un array di caratteri e apprenda (in qualche modo) che i byte rappresentano il testo con una certa codifica (ad es. ISO 8859-1, Windows-1252 o UTF-8). Come posso decodificare il contenuto di questo array di caratteri in una stringa Python?
La stringa Python dovrebbe in generale essere di tipo unicode
& # 8212; ad esempio, un 0x93
nell'input codificato di Windows-1252 diventa un u '\ u0201c '
.
Ho provato a usare PyString_Decode
, ma fallisce sempre quando ci sono caratteri non ASCII nella stringa. Ecco un esempio che fallisce:
#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;
}
Il messaggio di errore è UnicodeEncodeError: il codec 'ascii' non può codificare il carattere u '\ u201c' nella posizione 0: ordinale non compreso nell'intervallo (128)
, che indica che ascii
viene utilizzata anche se nella chiamata a PyString_Decode
viene specificato windows_1252
.
Il codice seguente risolve il problema usando PyString_FromString
per creare una stringa Python dei byte non codificati, quindi chiamando il suo metodo 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;
}
Soluzione
PyString_Decode fa questo:
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, fa sostanzialmente quello che stai facendo nel tuo secondo esempio: converte in una stringa, quindi decodifica la stringa. Il problema qui sorge da PyString_AsDecodedString, piuttosto che da PyString_AsDecodedObject. PyString_AsDecodedString esegue PyString_AsDecodedObject, ma tenta quindi di convertire l'oggetto unicode risultante in un oggetto stringa con la codifica predefinita (per te, sembra ASCII). Ecco dove fallisce.
Credo che dovrai fare due chiamate, ma puoi usare PyString_AsDecodedObject piuttosto che chiamare il pitone "decodifica". metodo. Qualcosa del tipo:
#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;
}
Non sono del tutto sicuro di quale sia il ragionamento alla base di PyString_Decode in questo modo. Un thread molto vecchio su python-dev sembra indica che ha qualcosa a che fare con il concatenamento dell'output, ma poiché i metodi Python non fanno lo stesso, non sono sicuro che sia ancora pertinente.
Altri suggerimenti
Non vuoi decodificare la stringa in una rappresentazione Unicode, vuoi solo trattarla come una matrice di byte, giusto?
Basta usare PyString_FromString
:
char *cstring;
PyObject *pystring = PyString_FromString(cstring);
Questo è tutto. Ora hai un oggetto Python str ()
. Consulta i documenti qui: https://docs.python.org/2/c- API / string.html
Sono un po 'confuso su come specificare " str " o " unicode. " Sono abbastanza diversi se hai caratteri non ASCII. Se vuoi decodificare una stringa C e sai esattamente in quale set di caratteri si trova, quindi sì, PyString_DecodeString
è un buon punto di partenza.
Prova a chiamare PyErr_Print ()
in il " if (! py_string)
" clausola. Forse l'eccezione di Python ti darà qualche informazione in più.