Python: ctypes, чтение POINTER(c_char) в python

У меня есть поле ctypes, которое является POINTER(c_char) (должно быть, согласно документации, c_char_p не работает для моего приложения: https://docs.python.org/3.7/library/ctypes.html#ctypes.c_char_p)

For a general character pointer that may also point to binary data, POINTER(c_char) must be used.

Тем не менее, это использование, рекомендованное самим ctypes, похоже, имеет недостаток: оно претендует на то, чтобы быть указателем на один символ, однако это не так, это указатель на массив байтов.

Как я могу прочитать массив, возвращаемый функцией ctypes (я знаю length) в Python? Попытка проиндексировать это как foo[0:len], где foo - POINTER(c_char) взрывается с TypeError: 'c_char' object is not subscriptable

Я могу напечатать первый символ строки байтов, используя либо print(foo), либо print(foo[0])

Я думал, что ctypes.cast может работать, однако я не знаю, как передать ему длину приведения (например, интерпретировать первые N байтов из адреса foo как объект bytes)

Обновлено: некоторый код.

Итак, у меня есть структура:

class foo(Structure):
_fields_ = [("state", c_int),
            ("type", c_int),
            ("len", c_int),
            ("payload", POINTER(c_char))]  # according to th following the python bytes are already unsinged https://bytes.com/topic/python/answers/695078-ctypes-unsigned-char

И у меня есть еще одна функция, которая возвращает POINTER(foo)

lib3 = CDLL(....so)
f = lib3.f
f.restype = POINTER(foo)

Я вызываю f, который возвращает POINTER(foo):

ptrf = f(....)

А потом я пытался получить доступ ptrf.payload. Работает следующий код:

def get_payload(ptr_to_foo):

    val = cast(ptr_to_foo.contents.payload, c_char_p).value
    return val[:ptr_to_foo.contents.len]

Так что я делаю

 ptrf = f(....)
 get_payload(ptrf)

Мне было интересно, будет ли проще написать функцию get_payload.

Можете поделиться кодом/подробностями (как называется функция, какие аргументы ей передаются, кто выделяет массив)? Может быть, весь подход неверен. Полученный Ошибка типа означает, что фу является c_char (а не указателем). Я могу индексировать ctypes.LP_c_char (который был создан из массива символов).

CristiFati 11.03.2019 15:45

Вопрос @CristiFati обновлен, чтобы включить больше кода

Tommy 12.03.2019 17:55
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
4 169
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если у вас действительно есть тип POINTER(c_char), он доступен для подписки. В будущем предоставьте код, который воспроизводит вашу проблему:

>>> p = cast(create_string_buffer(b'Hello, world!'),POINTER(c_char))
>>> p
<ctypes.LP_c_char object at 0x000001C2F6B58848>
>>> p[0]
b'H'
>>> p[:14]
b'Hello, world!\x00'
>>> cast(p,c_char_p).value  # only if known to be nul-terminated
b'Hello, world!'
Ответ принят как подходящий

Как указано в [Python.Docs]: ctypes — внешняя библиотека функций для Python, вы должны нет использовать c_char_p с двоичными данными.
Конечно, на это можно не обращать внимания, но тогда могут возникнуть сюрпризы (строка молча усекается).

Хотя это можно проиллюстрировать строками кода ~5, вставив все это целиком:

dll.c:

#include <stdlib.h>

#if defined(_WIN32)
#  define DLL_EXPORT __declspec(dllexport)
#else
#  define DLL_EXPORT
#endif

#define LEN 5


typedef struct CharPtrWrapperTag {
    int len;
    char *data;
} CharPtrWrapper;


DLL_EXPORT CharPtrWrapper *get() {
    CharPtrWrapper *ret = malloc(sizeof(CharPtrWrapper));
    ret->len = LEN;
    ret->data = malloc(LEN * sizeof(char));
    ret->data[0] = 'A';
    ret->data[1] = 'B';
    ret->data[2] = 0;
    ret->data[3] = 'C';
    ret->data[4] = 'D';
    return ret;
}


DLL_EXPORT void release(CharPtrWrapper *pWrap) {
    if (pWrap) {
        free(pWrap->data);
        pWrap->data = NULL;
        pWrap->len = 0;
        free(pWrap);
    }
}

код.py:

#!/usr/bin/env python3

import sys
import ctypes


DLL_NAME = "./dll.dll"
CharPtr = ctypes.POINTER(ctypes.c_char)

class CharPtrWrapper(ctypes.Structure):
    _fields_ = [
        ("len", ctypes.c_int),
        ("data", CharPtr),
    ]


CharPtrWrapperPtr = ctypes.POINTER(CharPtrWrapper)


def main():
    dll = ctypes.CDLL(DLL_NAME)
    get = dll.get
    get.restype = CharPtrWrapperPtr
    release = dll.release
    release.argtypes = [CharPtrWrapperPtr]
    wrap_ptr = get()
    wrap = wrap_ptr.contents
    print("{:}\n    Len: {:d}".format(wrap, wrap.len))
    for idx in range(wrap.len):
        print("        {:d}: {:}".format(idx, wrap.data[idx]))

    s = ctypes.cast(wrap.data, ctypes.c_char_p).value[:wrap.len]
    print("\nctypes.c_char_p cast: {:}".format(s))

    CharArr = ctypes.c_char * wrap.len
    char_arr = CharArr(*wrap.data[:wrap.len])
    print("CharArr: {:}".format(char_arr.raw))
    release(wrap_ptr)
    print("\nDone.")


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

Выход:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q055103298]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64

[prompt]> dir /b
code.py
dll.c

[prompt]> cl /nologo /DDLL /MD dll.c  /link /NOLOGO /DLL /OUT:dll.dll
dll.c
   Creating library dll.lib and object dll.exp

[prompt]> dir /b
code.py
dll.c
dll.dll
dll.exp
dll.lib
dll.obj

[prompt]> "e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

<__main__.CharPtrWrapper object at 0x000001279250D248>
    Len: 5
        0: b'A'
        1: b'B'
        2: b'\x00'
        3: b'C'
        4: b'D'

ctypes.c_char_p cast: b'AB'
CharArr: b'AB\x00CD'

Done.

это определенно спасло меня от огромной ошибки! Это отлично работает спасибо

Tommy 13.03.2019 15:17

здесь происходят какие-то волшебные вещи, *wrap.data[:wrap.len] выглядит как разыменование указателя, но это не так. Я немного изменил ваш ответ, чтобы вернуть char_arr[0:len] вместо того, чтобы печатать его. Может быть, мне тоже стоит попробовать этот атрибут raw.

Tommy 13.03.2019 15:24

Это распаковка кортежа (stackoverflow.com/questions/2238355/…). Срез [0:len] не нужен (то же самое, что и [:len] кстати :) ), потому что char_arr не будет длиннее Лен.

CristiFati 13.03.2019 15:30

но возврат без [0:len] возвращает объект ctypes char_array, тогда как нарезка, похоже, возвращает объект bytes. Вызывающий объект ожидает объект bytes, а не представление ctypes.

Tommy 13.03.2019 15:43

Затем верните char_array.raw (который оператор среза берет и (ненужно) нарезает). Но, в конце концов, делайте это так, как считаете лучшим. Я немного маньяк, когда речь заходит об эффективности, коде, запускаемом более одного раза, ....

CristiFati 13.03.2019 15:45

хорошо, возвращение в необработанном виде кажется заменой [0: len], поэтому придерживайтесь этого в соответствии с вашей рекомендацией.

Tommy 13.03.2019 16:26

Другие вопросы по теме