Как работает преобразование строк между строкой PyUnicode и строкой C?

У меня есть объект PyUnicode, который я пытаюсь преобразовать обратно в строку C (char *).

То, как я пытаюсь это сделать, похоже, не работает. Вот мой код:

PyObject * objectCompName = PyTuple_GET_ITEM(compTuple, (Py_ssize_t) 0);
PyObject * ooCompName = PyUnicode_AsASCIIString(objectCompName);
char * compName = PyBytes_AsString(ooCompName);
Py_DECREF(ooCompName);

Есть ли другой/лучший способ сделать это?


person ComputerLocus    schedule 18.03.2016    source источник
comment
Define, кажется, не работает. У вас бывают сбои? Результаты отличаются от желаемых? В любом случае, что именно?   -  person John Bollinger    schedule 18.03.2016
comment
С какой версией Python вы работаете?   -  person John Bollinger    schedule 18.03.2016
comment
@JohnBollinger ошибается, когда добирается сюда. Питон 3.   -  person ComputerLocus    schedule 18.03.2016
comment
Есть ли причина для отрицательного ответа как на мой вопрос, так и на ответ?   -  person ComputerLocus    schedule 18.03.2016
comment
Вы не проверяете возвращаемые значения ваших вызовов API и не проверяете, возникло ли исключение. Это было бы хорошим началом.   -  person John Bollinger    schedule 18.03.2016
comment
NMDV, но в вопросе не хватает деталей и воспроизводимости.   -  person John Bollinger    schedule 18.03.2016
comment
@JohnBollinger Не обязательно просить отладить мой код здесь. Я привел пример того, что я пробовал, и прошу найти решение для преобразования строки PyUnicode в Char * в C, поскольку мой не работает.   -  person ComputerLocus    schedule 18.03.2016
comment
связанные: stackoverflow.com/questions/6783493/   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 27.02.2020


Ответы (2)


Если char * в кодировке UTF-8 подходит, вам обязательно следует использовать PyUnicode_AsUTF8AndSize (для этого требуется Python 3.3):

PyObject * objectCompName = PySequence_GetItem(compTuple, 0);
if (! objectCompName) {
    return NULL;
}

Py_ssize_t size;
char *ptr = PyUnicode_AsUTF8AndSize(objectCompName, &size);
if (!ptr) {
    return NULL;
}

// notice that the string pointed to by ptr is not guaranteed to stay forever,
// and you need to copy it, perhaps by `strdup`.

Кроме того, помните, что обязательно проверять возвращаемое значение каждого Py* вызова функции, который вы когда-либо выполняли в своем коде.

Здесь PyTuple_GetItem вернет NULL, если compTuple не является tuple или 0 вызывает IndexError. PyUnicode_AsUTF8AndSize вернет NULL, если objectCompName не является объектом str. Игнорируйте возвращаемое значение, и CPython вылетит с ошибкой SIGSEGV, когда условия будут правильными.

person Antti Haapala    schedule 18.03.2016
comment
Строка PyUnicode изначально создается в другой функции C с использованием PyUnicode_DecodeASCII. - person ComputerLocus; 18.03.2016
comment
В этом случае PyUnicode_AsUTF8AndSize очень хорошо. - person Antti Haapala; 18.03.2016
comment
Хорошо, отлично. Вы заявляете, что обязательно проверять возвращаемые значения. Я планирую проверить их, просто я не был, так как хотел сначала просто протестировать и убедиться, что это работает. Действительно ли требуется проверять возвращаемое значение, или вы просто говорите, что это очень хорошая практика? - person ComputerLocus; 18.03.2016
comment
Да, это абсолютно обязательно, а не только рекомендуется. - person Antti Haapala; 18.03.2016
comment
Есть причина для этого? Я добавил эти проверки и реализовал ваше предложение, и оно работает. - person ComputerLocus; 18.03.2016
comment
Ха-ха, теперь при проверке objectCompName Python выдает исключение: SystemError: ../Objects/tupleobject.c:139: bad argument to internal function - person ComputerLocus; 18.03.2016
comment
Значит, ваш кортеж не был кортежем? - person Antti Haapala; 18.03.2016
comment
Правильно, так что это определенно хорошо, что вы сказали мне, что мне нужны эти проверки, поскольку теперь я вижу, что вызывает основные проблемы. Спасибо. - person ComputerLocus; 18.03.2016
comment
@Fogest, что SystemError выглядит некрасиво. Возможно, вместо этого используйте PySequence_GetItem, это также позволяет использовать list; дает TypeError, если это не последовательность - person Antti Haapala; 19.03.2016
comment
Проблема, похоже, в том, что это был список, а не кортеж. Вместо этого я заменил его на PyList, но я заменю его на PySequence, так как он выглядит еще безопаснее. И это устраняет эту проблему, но теперь она умирает позже при проверке на !ptr после вызова PyUnicode_AsUTF8AndSize: TypeError: bad argument type for built-in operation . Вы упомянули TypeError, но он выдает эту ошибку типа на этапе преобразования, а не при проверке области PySequence. - person ComputerLocus; 19.03.2016
comment
Я только что сделал PyUnicode_Check для objectCompName, и он не возвращает true. Однако у меня также есть несколько целых чисел, которые захвачены с использованием того же метода PySequence_GetItem(), и они возвращают true в PyLong_Check, поэтому с этими строками что-то происходит. - person ComputerLocus; 19.03.2016
comment
И я думаю, что знаю почему. Глупая ошибка с моей стороны. Спасибо за помощь. Вы решили мою проблему и помогли улучшить проверку ошибок! - person ComputerLocus; 19.03.2016

Сначала вам нужно преобразовать ваш Python PyUnicode в строку Python, отличную от Unicode (подробнее здесь: https://docs.python.org/2/c-api/unicode.html#ascii-codecs), то вы можете легко преобразовать результат в char* .

Ниже приведен псевдокод, который поможет вам продолжить:

// Assumption: you have a variable named "pyobj" which is
// a pointer to an instance of PyUnicodeObject.

PyObject* temp = PyUnicode_AsASCIIString(pyobj);
if (NULL == temp) {
    // Means the string can't be converted to ASCII, the codec failed
    printf("Oh noes\n");
    return;
}

// Get the actual bytes as a C string
char* c_str = PyByteArray_AsString(temp);

// Use the string in some manner
printf("The python unicode string is: %s\n", c_str);

// Make sure the temp stuff gets cleaned up at the end
Py_XDECREF(temp);
person user 12321    schedule 18.03.2016
comment
Строка с PyByteArray_AsString(temp); ошибочна. - person ComputerLocus; 18.03.2016