Python Ctypes: преобразовать список целых чисел в массив шорт - PullRequest
2 голосов
/ 04 мая 2020

Я пытаюсь преобразовать список целых чисел в массив шортов ctypes. Затем я хочу назначить этот массив полю в BigEndianStructure. Я попытался сделать это:

from ctypes import BigEndianStructure, c_uint16

class Test(BigEndianStructure):
    _pack_ = 1
    _fields_ = [('arr', c_uint16 * 10)]

num_list = [45, 56, 23]
tester = Test()
short_array = c_uint16 * 10
tester.arr = short_array.from_buffer_copy(bytes(num_list))

Но мне не понравилось, что список оказался меньше ожидаемого:

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    tester.arr = short_array.from_buffer_copy(bytes(num_list))
ValueError: Buffer size too small (3 instead of at least 20 bytes)

Итак, я попытался расширить список и преобразовать от байтов до старшего байта:

new_list = num_list[:10] + [0]*(10-len(num_list))
buffer = b''
for item in new_list:
    buffer += item.to_bytes(2, byteorder='big')
tester.arr = short_array.from_buffer_copy(buffer)

Но он жалуется на то, что буфер не является "be_array", что, как я предполагаю, связано с порядком байтов:

Traceback (most recent call last):
  File "test.py", line 14, in <module>
    tester.arr = short_array.from_buffer_copy(buffer)
TypeError: incompatible types, c_ushort_Array_10 instance instead of c_ushort_be_Array_10 instance

Я продумывать это? Кто-нибудь есть какие-либо предложения о том, как обойти это?

Редактировать: пояснения из комментариев, соответствующая структура в C имеет uint16_t arr [MAX_LEN], где MAX_LEN = 10. Поэтому я хочу отправить заполненный 0 массив, если переданный массив не полный MAX_LEN.

Ответы [ 2 ]

3 голосов
/ 05 мая 2020

Существует минимальная поддержка BigEndianStructure. Вы не можете создать c_ushort_be или c_ushort_be_Array_10, но вы можете назначить фрагмент строки, если ваш список короче вашего массива, и он будет делать правильные вещи:

from ctypes import *
from binascii import hexlify

class Test(BigEndianStructure):
    _fields_ = [('arr', c_uint16 * 10)]

num_list = [45, 56, 23]
tester = Test()
tester.arr[:len(num_list)] = num_list
print(hexlify(bytes(tester)))

Вывод (шестнадцатеричное представление необработанной структуры):

b'002d003800170000000000000000000000000000'

См. также модуль struct . Это может удовлетворить ваши потребности.

0 голосов
/ 05 мая 2020

Я обдумываю это?

Да, массово. Нет необходимости связываться с from_buffer_copy или bytes. Если единственной причиной, по которой вам нужна структура с прямым порядком байтов, является то, что вы находитесь в системе с прямым порядком байтов, то использование обычной структуры вместо этого сделает вашу жизнь намного проще:

from ctypes import Structure, c_uint16

class Test(Structure):
    _pack_ = 1
    _fields_ = [('arr', c_uint16 * 10)]

num_list = [45, 56, 23]
tester = Test()
short_array = c_uint16 * 10
tester.arr = short_array(*num_list) # Or num_list[:10] if it might be too long

Если вы действительно хотите использовать метод с прямым порядком байтов, даже если вы используете систему с прямым порядком байтов, то все становится сложнее. Прежде всего, это приведет к тому, что 45, 56 и 23 станут 11520, 14336 и 5888 в C. Если вы не хотите, чтобы это произошло, то вам нужно вышеуказанное решение. Если это действительно то, что вы хотите, тогда читайте дальше. В этом случае short_array = c_uint16 * 10 для вас бесполезен, так как имеет собственный порядок байтов, но массив, который вам нужно поместить в структуру, должен иметь порядок байтов. Я не знаю, как заставить массив массива с прямым порядком байтов создавать все сразу, поэтому вам нужно будет заполнить его вручную, например:

from ctypes import BigEndianStructure, c_uint16

class Test(BigEndianStructure):
    _pack_ = 1
    _fields_ = [('arr', c_uint16 * 10)]

num_list = [45, 56, 23]
tester = Test()
for i, x in enumerate(num_list): # Or num_list[:10] if it might be too long
  tester.arr[i] = x
...