Ошибка генерирования create_string_buffer ТипError: ожидаемая строка/байты вместо str instance

Я пытаюсь сделать этот простой пример ctypes и получить упомянутую ошибку

>>> from ctypes import create_string_buffer
>>> str = create_string_buffer("hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python32\lib\ctypes\__init__.py", line 59, in create_string_buffer
buf.value = init
TypeError: str/bytes expected instead of str instance

Кто-нибудь знает, что я делаю неправильно?

В той же заметке я пытаюсь передать указатель на строку в функцию C из моего кода на Python, чтобы я мог выполнить там некоторую операцию с строкой и вернуть другую строку. Может кто-нибудь дать мне пример кода о том, как это сделать?

extern "C" __declspec(dllexport) char * echo(char* c)
{
      // do stuff
    return c;
}

Ответ 1

Что касается его работы, если вы передаете ему объект bytes, он работает:

>>> import ctypes
>>> ctypes.create_string_buffer(b'hello')
<ctypes.c_char_Array_6 object at 0x25258c0>

Глядя на код для create_string_buffer:

def create_string_buffer(init, size=None):
    """create_string_buffer(aBytes) -> character array
    create_string_buffer(anInteger) -> character array
    create_string_buffer(aString, anInteger) -> character array
    """
    if isinstance(init, (str, bytes)):
        if size is None:
            size = len(init)+1
        buftype = c_char * size
        buf = buftype()
        buf.value = init
        return buf
    elif isinstance(init, int):
        buftype = c_char * init
        buf = buftype()
        return buf
    raise TypeError(init)

Выполняя это прямо,

>>> (ctypes.c_char * 10)().value = b'123456789'

Это отлично работает.

>>> (ctypes.c_char * 10)().value = '123456789'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: str/bytes expected instead of str instance

Это показывает то же поведение. Мне кажется, что вы нашли ошибку.

Время для посещения http://bugs.python.org. Есть несколько ошибок, связанных с c_char и create_string_buffer, которые находятся в одном и том же поле, но ни одна из них не сообщила, что дает ему str, теперь не удается (но есть определенные примеры, показывающие, что он использовался для работы в Py3K).

Ответ 2

Вы используете Python 3, а строки (str -type) в Python 3 - это объекты Unicode. create_string_buffer создает массивы C char, поэтому вам необходимо передать объект bytes. Если у вас есть объект str encode с соответствующей кодировкой для создания объекта bytes. Обратите внимание: существует create_unicode_buffer, который также создает массивы C wchar и принимает объекты Python 3 str.

Python 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.create_unicode_buffer('abcd')
<ctypes.c_wchar_Array_5 object at 0x00C2D300>
>>> ctypes.create_string_buffer(b'abcd')
<ctypes.c_char_Array_5 object at 0x00C2D1C0>
>>> ctypes.create_string_buffer('abcd'.encode('utf-8'))
<ctypes.c_char_Array_5 object at 0x00C2D300>

Что касается второй части вашего вопроса, вы хотите отредактировать существующую строку или вернуть совершенно новую строку, выделенную в куче? Если первая, используя create_string_buffer для передачи изменчивой строки функции и изменения ее на месте, будет работать. Вот простой пример вызова _strupr() из библиотеки Windows MSVCRT:

Python 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> c = ctypes.CDLL('msvcr90.dll')
>>> strupr = c._strupr
>>> a=ctypes.create_string_buffer(b'abc')
>>> strupr.restype=ctypes.c_char_p
>>> strupr(a)
b'ABC'

Примечание: установка restype (тип результата) функции на c_char_p указывает ctypes для преобразования возвращаемого значения в объект bytes. Также обратите внимание, что _strupr преобразует строку на место, поэтому должен быть передан изменяемый строковый буфер. Только передайте байтовые строки Python в C-функции, которые принимают const char*, так как изменение строк Python непосредственно через ctypes равно Bad TM.

Ответ 3

Аргумент - это размер буфера, необходимый для строки, поэтому передайте ему длину, в которой должен быть буфер. Он создаст массив char этого размера.

>>> from ctypes import create_string_buffer
>>> buf = create_string_buffer(20)
>>> buf
<ctypes.c_char_Array_20 object at 0x355348>
>>>