2008-10-17 24 views
7

Mam wbudowany interpreter Python w programie C. Załóżmy, że program C odczytuje niektóre bajty z pliku do tablicy char i uczy się (w jakiś sposób), że bajty reprezentują tekst o określonym kodowaniu (np. ISO 8859-1, Windows-1252 lub UTF-8). Jak odszyfrować zawartość tablicy char w łańcuchu Pythona?Jak przekonwertować łańcuch C (tablicę znaków) na ciąg znaków w języku Python, gdy w ciągu znaków występują znaki spoza zestawu znaków ASCII?

Łańcuch w języku Python powinien być generalnie typu unicode - na przykład kod 0x93 w formacie Windows-1252 staje się u'\u0201c'.

Podjęto próbę użycia PyString_Decode, ale zawsze kończy się niepowodzeniem, gdy w ciągu występują znaki spoza ASCII. Oto przykład, który kończy się niepowodzeniem:

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

Komunikat o błędzie jest UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128), co oznacza, że ​​kodujące ascii służy chociaż określić windows_1252 w zaproszeniu do PyString_Decode.

Poniższy kod działa wokół problemu za pomocą PyString_FromString aby utworzyć ciąg Pythona z niezdekodowane bajtów, a następnie wywołanie decode metody:

#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

Aby wybrać Nit, łańcuch C to znak char [], a nie znak * –

+1

Aby wybrać nit-pick, odwołanie do wartości nie ma znaczenia. W każdym razie tablice są przekazywane jako wskaźniki do funkcji. – gnud

Odpowiedz

6

PyString_Decode robi to:

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, robi w zasadzie to, co robisz w swoim drugim przykładzie - konwertuje do łańcucha, a następnie zdekodować ciąg. Problem tutaj wynika z PyString_AsDecodedString, a nie PyString_AsDecodedObject. PyString_AsDecodedString robi PyString_AsDecodedObject, ale potem próbuje przekonwertować wynikowy obiekt Unicode na obiekt tekstowy z domyślnym kodowaniem (dla ciebie wygląda na to ASCII). Tam się nie udaje.

Uważam, że będziesz musiał wykonać dwa połączenia - ale możesz użyć PyString_AsDecodedObject zamiast wywoływać pythonową metodę "dekodowania". Coś jak:

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

Nie jestem całkowicie pewien, co rozumowanie PyString_Decode działa w ten sposób. Wydaje się, że kod very old thread on python-dev ma coś wspólnego z łańcuchem wyjściowym, ale ponieważ metody Pythona nie robią tego samego, nie jestem pewien, czy to nadal ma znaczenie.

+0

Opps! Dzięki Ljosa; naprawiony. –

+0

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

3

nie chcesz zdekodować ciąg Unicode do reprezentacji , chcesz go traktować jako tablicę bajtów, prawda?

Wystarczy użyć PyString_FromString:

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

to wszystko. Teraz masz obiekt Python str(). Zobacz dokumentację tutaj: https://docs.python.org/2/c-api/string.html

Jestem trochę zdezorientowany, jak określić "str" ​​lub "unicode". Są zupełnie inne, jeśli masz znaki spoza ASCII. Jeśli chcesz dekodować ciąg znaków C i wiesz dokładnie, jaki zestaw znaków jest w nim, to tak, PyString_DecodeString jest dobrym miejscem do rozpoczęcia.

+0

Chcę go faktycznie zdekodować, więc niezależnie od tego, jaki kod Pythona kończy się za pomocą ciągu znaków, nie trzeba wiedzieć, w jaki sposób został pierwotnie zakodowany (w danych wejściowych do programu C). Dziękuję za wskazanie, że byłem niejasny; Zmieniłem moje pytanie. –

2

Spróbuj zadzwonić pod numer PyErr_Print() w "". Być może wyjątek Pythona da ci trochę więcej informacji.

+0

Dzięki, zrobiłem i włączyłem informacje do pytania. –

+0

Bez problemu. Jeśli ta rada byłaby pomocna, byłbym wdzięczny za przegłosowanie. :-) –