2008-10-17 18 views
7

Tôi đã nhúng trình thông dịch Python vào chương trình C. Giả sử chương trình C đọc một số byte từ một tệp vào một mảng char và học (bằng cách nào đó) rằng các byte biểu diễn văn bản với một mã hóa nhất định (ví dụ: ISO 8859-1, Windows-1252 hoặc UTF-8). Làm thế nào để giải mã nội dung của mảng char này thành một chuỗi Python?Làm thế nào để chuyển đổi một chuỗi C (mảng char) thành một chuỗi Python khi có các ký tự không phải ASCII trong chuỗi?

Chuỗi Python nói chung phải thuộc loại unicode — ví dụ: 0x93 trong đầu vào được mã hóa Windows-1252 trở thành u'\u0201c'.

Tôi đã cố gắng sử dụng PyString_Decode, nhưng nó luôn thất bại khi có các ký tự không phải ASCII trong chuỗi. Dưới đây là một ví dụ thất bại:

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

Các thông báo lỗi là UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128), mà chỉ ra rằng mã hóa ascii được sử dụng mặc dù chúng tôi chỉ định windows_1252 trong cuộc gọi đến PyString_Decode.

Các mã sau đây làm việc xung quanh vấn đề bằng cách sử dụng PyString_FromString để tạo ra một chuỗi Python của các byte undecoded, sau đó gọi phương thức decode của nó:

#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

Để chọn nit, chuỗi C là char [], không phải là ký tự char * –

+1

Để chọn nit, khi tham chiếu một giá trị, nó không quan trọng. Tuy nhiên, các mảng được chuyển như các con trỏ tới các hàm. – gnud

Trả lời

6

PyString_Decode thực hiện điều này:

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, nó cơ bản những gì bạn đang làm trong ví dụ thứ hai của bạn - chuyển đổi thành một chuỗi, sau đó giải mã chuỗi. Vấn đề ở đây phát sinh từ PyString_AsDecodedString, thay vì PyString_AsDecodedObject. PyString_AsDecodedString hiện PyString_AsDecodedObject, nhưng sau đó cố gắng để chuyển đổi các đối tượng unicode kết quả thành một đối tượng chuỗi với mã hóa mặc định (cho bạn, trông giống như đó là ASCII). Đó là nơi nó thất bại.

Tôi tin rằng bạn sẽ cần thực hiện hai cuộc gọi - nhưng bạn có thể sử dụng PyString_AsDecodedObject thay vì gọi phương thức "giải mã" python. Một cái gì đó như:

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

Tôi không hoàn toàn chắc chắn lý do đằng sau PyString_Decode hoạt động theo cách này là gì. Một very old thread on python-dev dường như chỉ ra rằng nó có một cái gì đó để làm với chuỗi đầu ra, nhưng kể từ khi các phương pháp Python không làm như vậy, tôi không chắc chắn nếu đó là vẫn còn có liên quan.

+0

Opps! Cảm ơn Ljosa; đã sửa. –

+0

Đối với Python3 https://docs.python.org/3.5/c-api/unicode.html#c.PyUnicode_FromString – crizCraig

3

Bạn không muốn để giải mã các chuỗi thành một đại diện Unicode , bạn chỉ muốn coi nó như là một mảng các byte, đúng không?

Chỉ cần sử dụng PyString_FromString:

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

Đó là tất cả. Bây giờ bạn có một đối tượng Python str(). Xem tài liệu tại đây: https://docs.python.org/2/c-api/string.html

Tôi hơi bối rối về cách chỉ định "str" ​​hoặc "unicode". Chúng hoàn toàn khác nếu bạn có các ký tự không phải ASCII. Nếu bạn muốn giải mã một chuỗi C bạn biết chính xác ký tự nào được đặt trong đó, thì có, PyString_DecodeString là một nơi tốt để bắt đầu.

+0

Tôi muốn thực sự giải mã nó, vì vậy bất kỳ mã Python nào kết thúc bằng cách sử dụng chuỗi không cần phải biết nó được mã hóa ban đầu như thế nào (trong đầu vào cho chương trình C). Cảm ơn bạn đã chỉ ra rằng tôi không rõ ràng; Tôi đã chỉnh sửa câu hỏi của mình. –

2

Hãy thử gọi PyErr_Print() trong mệnh đề "if (!py_string)". Có lẽ ngoại lệ python sẽ cung cấp cho bạn một số thông tin.

+0

Cảm ơn, tôi đã làm và kết hợp thông tin vào câu hỏi. –

+0

Không sao cả. Nếu lời khuyên là hữu ích, tôi sẽ đánh giá cao một upvote. :-) –