كيفية تحويل سلسلة C (صفيف char) إلى سلسلة بيثون عندما يكون هناك أحرف غير ASCII في السلسلة؟
-
03-07-2019 - |
سؤال
لقد قمت بتضمين مترجم بيثون في برنامج C. لنفترض أن برنامج C يقرأ بعض البايتات من ملف إلى صفيف Char ويتعلم (بطريقة ما) أن البايتات تمثل النص مع ترميز معين (على سبيل المثال ، ISO 8859-1 أو Windows-1252 أو UTF-8). كيف يمكنني فك تشفير محتويات صفيف Char في سلسلة Python؟
يجب أن تكون سلسلة Python بشكل عام من النوع unicode
- على سبيل المثال ، أ 0x93
في Windows-1252 ، يصبح المدخلات المشفرة u'\u0201c'
.
لقد حاولت الاستخدام PyString_Decode
, ، لكنه يفشل دائمًا عندما يكون هناك أحرف غير ASCII في السلسلة. إليك مثال يفشل:
#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;
}
رسالة الخطأ هي UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128)
, مما يشير إلى أن ascii
يتم استخدام الترميز على الرغم من أننا نحدد windows_1252
في الدعوة إلى PyString_Decode
.
الرمز التالي يعمل حول المشكلة باستخدام PyString_FromString
لإنشاء سلسلة بيثون من البايتات غير المشفرة ، ثم استدعاءها 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;
}
المحلول
Pystring_decode يفعل هذا:
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 ، يفعل بشكل أساسي ما تفعله في مثالك الثاني - يتحول إلى سلسلة ، ثم فك تشفير السلسلة. تنشأ المشكلة هنا من pystring_asdecodedString ، بدلاً من pystring_asdecodedObject. Pystring_asdecodedString يقوم pystring_asdecodedobject ، ولكن بعد ذلك يحاول تحويل كائن Unicode الناتج إلى كائن سلسلة مع الترميز الافتراضي (بالنسبة لك ، يبدو أن هذا ASCII). هذا هو المكان الذي فشل فيه.
أعتقد أنك ستحتاج إلى إجراء مكالمتين - ولكن يمكنك استخدام pystring_asdecodedObject بدلاً من استدعاء طريقة "فك الشفرة" Python. شيء مثل:
#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;
}
لست متأكدًا تمامًا مما هو السبب وراء عمل Pystring_Decode بهذه الطريقة. أ موضوع قديم جدا على بيثون ديف يبدو أنه يشير إلى أنه له علاقة بسلاسل الإخراج ، ولكن نظرًا لأن أساليب Python لا تفعل الشيء نفسه ، لست متأكدًا مما إذا كان ذلك لا يزال ذا صلة.
نصائح أخرى
أنت لا تريد فك تشفير السلسلة في تمثيل Unicode ، فأنت تريد فقط معاملتها كمجموعة من البايتات ، أليس كذلك؟
فقط استخدم PyString_FromString
:
char *cstring;
PyObject *pystring = PyString_FromString(cstring);
هذا كل شئ. الآن لديك بيثون str()
هدف. انظر المستندات هنا: https://docs.python.org/2/c-api/string.html
أنا مرتبك قليلاً حول كيفية تحديد "Str" أو "Unicode". فهي مختلفة تمامًا إذا كان لديك شخصيات غير ASCII. إذا كنت تريد فك تشفير سلسلة C و أنت تعرف بالضبط ما هي الشخصية التي تم تعيينها ، ثم نعم ، PyString_DecodeString
هو مكان جيد للبدء.
حاول الاتصال PyErr_Print()
في ال "if (!py_string)
"بند. ربما يعطيك استثناء بيثون بعض المعلومات.