struct — Интерпретируйте байты как упакованные двоичные данные

Источник: Lib/struct.py


Этот модуль конвертирует между значениями Python и структурами C, представленными как объекты Python bytes. Компактные format strings описывают предполагаемые преобразования в/из значений Python. Функции и объекты модуля могут быть использованы для двух разных целей: обмена данными с внешними источниками (файлами или сетевыми соединениями) или передачи данных между приложением на Python и слоем C.

Примечание

Если префиксный символ не указан, по умолчанию используется режим native. Он упаковывает или распаковывает данные в зависимости от платформы и компилятора, на котором был создан интерпретатор Python. Результат упаковки данной C-структуры включает байты-заглушки, которые поддерживают правильное выравнивание для соответствующих C-типов; аналогичным образом выравнивание учитывается при распаковке. В отличие от этого, при передаче данных между внешними источниками программист отвечает за определение порядка следования байтов и прокладок между элементами. Подробности см. в разделе Порядок, размер и выравнивание байтов.

Некоторые функции struct (и методы Struct) принимают аргумент буфер. Это относится к объектам, реализующим Буферный протокол и предоставляющим буфер для чтения или записи. Чаще всего для этой цели используются типы bytes и bytearray, но многие другие типы, которые можно рассматривать как массив байтов, реализуют протокол буфера, так что их можно читать/заполнять без дополнительного копирования из объекта bytes.

Функции и исключения

В модуле определены следующие исключения и функции:

exception struct.error

Исключение, возникающее в различных случаях; аргумент - строка, описывающая, что именно не так.

struct.pack(format, v1, v2, ...)

Возвращает объект bytes, содержащий значения v1, v2, …, упакованные в соответствии со строкой формата format. Аргументы должны точно соответствовать значениям, требуемым форматом.

struct.pack_into(format, buffer, offset, v1, v2, ...)

Упакуйте значения v1, v2, … в соответствии со строкой формата format и запишите упакованные байты в записываемый буфер buffer, начиная с позиции offset. Обратите внимание, что offset является обязательным аргументом.

struct.unpack(format, buffer)

Распаковать из буфера buffer (предположительно упакованного по pack(format, ...)) в соответствии со строкой формата format. Результатом является кортеж, даже если он содержит ровно один элемент. Размер буфера в байтах должен соответствовать размеру, требуемому форматом, что отражено в calcsize().

struct.unpack_from(format, /, buffer, offset=0)

Распаковать из буфера, начиная с позиции offset, в соответствии со строкой формата format. Результатом является кортеж, даже если он содержит ровно один элемент. Размер буфера в байтах, начиная с позиции offset, должен быть не меньше размера, требуемого форматом, что отражается calcsize().

struct.iter_unpack(format, buffer)

Итеративно распакуйте из буфера buffer в соответствии со строкой формата format. Эта функция возвращает итератор, который будет считывать из буфера одинаковые по размеру фрагменты, пока все его содержимое не будет израсходовано. Размер буфера в байтах должен быть кратен размеру, требуемому форматом, что отражается calcsize().

Каждая итерация выдает кортеж, заданный строкой формата.

Added in version 3.4.

struct.calcsize(format)

Возвращает размер структуры (и, следовательно, объекта bytes, создаваемого pack(format, ...)), соответствующей строке формата format.

Строки формата

Строки формата описывают расположение данных при упаковке и распаковке. Они строятся из format characters, которые определяют тип упаковываемых/распаковываемых данных. Кроме того, специальные символы управляют byte order, size and alignment. Каждая строка формата состоит из необязательного префиксного символа, который описывает общие свойства данных, и одного или нескольких символов формата, которые описывают фактические значения данных и вставку.

Порядок, размер и выравнивание байтов

По умолчанию типы C представляются в собственном формате и порядке байтов машины, а также правильно выравниваются, пропуская при необходимости байты-заглушки (в соответствии с правилами, используемыми компилятором C). Такое поведение выбрано для того, чтобы байты упакованной структуры точно соответствовали разметке памяти соответствующей C-структуры. Использовать ли «родное» упорядочивание байтов и вставку байтов или стандартные форматы, зависит от приложения.

В качестве альтернативы, первый символ строки формата может использоваться для указания порядка байт, размера и выравнивания упакованных данных, в соответствии со следующей таблицей:

Персонаж

Порядок байтов

Размер

Выравнивание

@

родной

родной

родной

=

родной

стандарт

нет

<

little-endian

стандарт

нет

>

big-endian

стандарт

нет

!

сеть (= big-endian)

стандарт

нет

Если первый символ не является одним из них, предполагается '@'.

Примечание

Число 1023 (0x3ff в шестнадцатеричной системе) имеет следующие представления байтов:

  • 03 ff в big-endian (>)

  • ff 03 в little-endian (<)

Пример из Python:

>>> import struct
>>> struct.pack('>h', 1023)
b'\x03\xff'
>>> struct.pack('<h', 1023)
b'\xff\x03'

Родной порядок байтов - big-endian или little-endian, в зависимости от хост-системы. Например, Intel x86, AMD64 (x86-64) и Apple M1 имеют little-endian; IBM z и многие устаревшие архитектуры имеют big-endian. Используйте sys.byteorder, чтобы проверить эндианальность вашей системы.

Родной размер и выравнивание определяются с помощью выражения sizeof компилятора C. Оно всегда сочетается с родным порядком байтов.

Стандартный размер зависит только от символа формата; см. таблицу в разделе Символы формата.

Обратите внимание на разницу между '@' и '=': оба используют собственный порядок байтов, но размер и выравнивание последнего стандартизированы.

Форма '!' представляет порядок байтов в сети, который всегда является big-endian, как определено в IETF RFC 1700.

Не существует способа указать неродной порядок байтов (принудительная замена байтов); используйте соответствующий выбор '<' или '>'.

Примечания:

  1. Подшивка автоматически добавляется только между последовательными членами структуры. В начале и в конце закодированной структуры добавление вставки не производится.

  2. При использовании неродного размера и выравнивания, например, при использовании символов „<“, „>“, „=“ и „!“, добавление подкладки не производится.

  3. Чтобы выровнять конец структуры в соответствии с требованиями выравнивания определенного типа, завершите формат кодом этого типа с нулевым числом повторений. См. Примеры.

Символы формата

Символы формата имеют следующее значение; преобразование между значениями C и Python должно быть очевидным, учитывая их типы. Столбец „Standard size“ означает размер упакованного значения в байтах при использовании стандартного размера; то есть когда строка формата начинается с одного из '<', '>', '!' или '='. При использовании родного размера размер упакованного значения зависит от платформы.

Формат

Тип C

Тип Python

Стандартный размер

Примечания

x

байт подложки

нет значения

(7)

c

char

байт длины 1

1

b

signed char

целое число

1

(1), (2)

B

unsigned char

целое число

1

(2)

?

_Bool

bool

1

(1)

h

short

целое число

2

(2)

H

unsigned short

целое число

2

(2)

i

int

целое число

4

(2)

I

unsigned int

целое число

4

(2)

l

long

целое число

4

(2)

L

unsigned long

целое число

4

(2)

q

long long

целое число

8

(2)

Q

unsigned long long

целое число

8

(2)

n

ssize_t

целое число

(3)

N

size_t

целое число

(3)

e

(6)

float

2

(4)

f

float

float

4

(4)

d

double

float

8

(4)

s

char[]

байты

(9)

p

char[]

байты

(8)

P

void*

целое число

(5)

Изменено в версии 3.3: Добавлена поддержка форматов 'n' и 'N'.

Изменено в версии 3.6: Добавлена поддержка формата 'e'.

Примечания:

  1. Код преобразования '?' соответствует типу _Bool, определенному в C99. Если этот тип недоступен, он моделируется с помощью char. В стандартном режиме он всегда представлен одним байтом.

  2. При попытке упаковать нецелое число с помощью любого из кодов преобразования целых чисел, если у нецелого числа есть метод __index__(), то этот метод вызывается для преобразования аргумента в целое число перед упаковкой.

    Изменено в версии 3.2: Добавлено использование метода __index__() для нецелых чисел.

  3. Коды преобразования 'n' и 'N' доступны только для собственного размера (выбранного по умолчанию или с помощью символа порядка байтов '@'). Для стандартного размера вы можете использовать любой другой целочисленный формат, подходящий для вашего приложения.

  4. Для кодов преобразования 'f', 'd' и 'e' упакованное представление использует формат IEEE 754 binary32, binary64 или binary16 (для 'f', 'd' или 'e' соответственно), независимо от формата с плавающей точкой, используемого платформой.

  5. Символ формата 'P' доступен только для собственного порядка байтов (выбранного по умолчанию или с помощью символа порядка байтов '@'). Символ порядка байтов '=' выбирает, какой порядок байтов использовать - little- или big-endian - в зависимости от хост-системы. Модуль struct не интерпретирует это как собственное упорядочивание, поэтому формат 'P' недоступен.

  6. Тип IEEE 754 binary16 «половинной точности» был введен в 2008 году в ревизии IEEE 754 standard. Он имеет бит знака, 5-битную экспоненту и 11-битную точность (с явным сохранением 10 бит), и может представлять числа между приблизительно 6.1e-05 и 6.5e+04 с полной точностью. Этот тип не очень широко поддерживается компиляторами языка Си: на типичной машине беззнаковое короткое число можно использовать для хранения, но не для математических операций. Более подробную информацию см. на странице Википедии, посвященной half-precision floating-point format.

  7. При упаковке 'x' вставляет один байт NUL.

  8. Символ формата 'p' кодирует «строку Паскаля», то есть короткую строку переменной длины, хранящуюся в фиксированном количестве байт, задаваемом счетчиком. Первый сохраненный байт - это длина строки или 255, в зависимости от того, что меньше. Далее следуют байты строки. Если строка, переданная в pack(), слишком длинная (длиннее, чем счетчик минус 1), сохраняются только первые count-1 байта строки. Если строка короче count-1, она заполняется нулевыми байтами, так что всего будет использовано ровно столько байт. Обратите внимание, что для unpack() символ формата 'p' занимает count байт, но возвращаемая строка никогда не может содержать более 255 байт.

  9. Для символа формата 's' счет интерпретируется как длина байта, а не как счетчик повторений, как для других символов формата; например, '10s' означает одну 10-байтную строку, отображающуюся на одну байтную строку Python или из нее, в то время как '10c' означает 10 отдельных однобайтовых элементов символов (например, cccccccccc), отображающихся на десять различных байтовых объектов Python или из них. (Конкретную демонстрацию разницы см. в Примеры.) Если счетчик не указан, по умолчанию он равен 1. При упаковке строка усекается или заполняется нулевыми байтами, чтобы она поместилась. При распаковке результирующий объект bytes всегда содержит ровно указанное количество байтов. В качестве особого случая '0s' означает одиночную пустую строку (в то время как '0c' означает 0 символов).

Символу формата может предшествовать интегральный счетчик повторов. Например, строка формата '4h' означает то же самое, что и 'hhhh'.

Символы пробелов между форматами игнорируются; однако граф и его формат не должны содержать пробелов.

При упаковке значения x с помощью одного из целочисленных форматов ('b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q'), если x находится вне допустимого диапазона для данного формата, то происходит повышение значения struct.error.

Изменено в версии 3.1: Ранее некоторые форматы целых чисел обворачивали значения, выходящие за пределы диапазона, и выводили DeprecationWarning вместо struct.error.

Для символа формата '?' возвращаемым значением будет либо True, либо False. При упаковке используется истинностное значение объекта-аргумента. Упаковывается либо 0, либо 1 в родном или стандартном представлении bool, а при распаковке любое ненулевое значение будет True.

Примеры

Примечание

Примеры нативного порядка байтов (обозначенные префиксом формата '@' или отсутствием какого-либо префикса) могут не совпадать с тем, что выдает машина читателя, поскольку это зависит от платформы и компилятора.

Упаковывайте и распаковывайте целые числа трех разных размеров, используя упорядочивание big endian:

>>> from struct import *
>>> pack(">bhl", 1, 2, 3)
b'\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
>>> calcsize('>bhl')
7

Попытка упаковать целое число, которое слишком велико для определенного поля:

>>> pack(">h", 99999)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
struct.error: 'h' format requires -32768 <= number <= 32767

Продемонстрируйте разницу между символами формата 's' и 'c':

>>> pack("@ccc", b'1', b'2', b'3')
b'123'
>>> pack("@3s", b'123')
b'123'

Распакованные поля можно назвать, присвоив их переменным или обернув результат в именованный кортеж:

>>> record = b'raymond   \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = unpack('<10sHHb', record)

>>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name=b'raymond   ', serialnum=4658, school=264, gradelevel=8)

Порядок следования символов формата может влиять на размер в родном режиме, так как подстановка неявна. В стандартном режиме пользователь сам отвечает за вставку желаемых символов. Обратите внимание, что в первом вызове pack после упакованного '#' были добавлены три байта NUL, чтобы выровнять следующее целое число по границе четырех байт. В этом примере вывод был произведен на машине с малым эндианом:

>>> pack('@ci', b'#', 0x12131415)
b'#\x00\x00\x00\x15\x14\x13\x12'
>>> pack('@ic', 0x12131415, b'#')
b'\x15\x14\x13\x12#'
>>> calcsize('@ci')
8
>>> calcsize('@ic')
5

Следующий формат 'llh0l' приводит к тому, что в конце добавляются два байта pad, предполагая, что лонги платформы выровнены по 4-байтовым границам:

>>> pack('@llh0l', 1, 2, 3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00'

См.также

Модуль array

Упакованное двоичное хранение однородных данных.

Модуль json

Кодировщик и декодер JSON.

Модуль pickle

Сериализация объектов в Python.

Приложения

Существует два основных применения модуля struct: обмен данными между кодом на Python и C внутри приложения или другого приложения, скомпилированного с помощью того же компилятора (native formats), и обмен данными между приложениями с использованием согласованной компоновки данных (standard formats). Вообще говоря, строки формата, создаваемые для этих двух областей, отличаются друг от друга.

Родные форматы

При построении строк формата, имитирующих родную раскладку, компилятор и архитектура машины определяют порядок байтов и размер данных. В таких случаях следует использовать символ формата @ для указания собственного порядка байтов и размера данных. Внутренние байты-заглушки обычно вставляются автоматически. Возможно, в конце строки формата потребуется код формата с нулевым повтором для округления до нужной границы байта для правильного выравнивания последовательных блоков данных.

Рассмотрим два простых примера (на 64-битной, little-endian машине):

>>> calcsize('@lhl')
24
>>> calcsize('@llh')
18

В конце второй строки формата данные не дотягиваются до границы 8 байт без использования дополнительной прокладки. Код формата с нулевым повтором решает эту проблему:

>>> calcsize('@llh0l')
24

Для указания повтора можно использовать код формата 'x', но для родных форматов лучше использовать формат с нулевым повтором, например '0l'.

По умолчанию используется собственное упорядочивание и выравнивание байтов, но лучше быть явным и использовать символ префикса '@'.

Стандартные форматы

При обмене данными за пределами вашего процесса, например, в сети или хранилище, будьте точны. Указывайте точный порядок байтов, размер и выравнивание. Не предполагайте, что они соответствуют «родному» порядку конкретной машины. Например, порядок байтов в сети - big-endian, а во многих популярных процессорах - little-endian. Явное определение этого порядка позволяет пользователю не задумываться о специфике платформы, на которой выполняется его код. Первый символ обычно должен быть < или > (или !). За добавление символов отвечает программист. Символ формата с нулевым повтором не работает. Вместо этого пользователь должен явно добавить байт 'x', где это необходимо. Обращаясь к примерам из предыдущего раздела, мы имеем:

>>> calcsize('<qh6xq')
24
>>> pack('<qh6xq', 1, 2, 3) == pack('@lhl', 1, 2, 3)
True
>>> calcsize('@llh')
18
>>> pack('@llh', 1, 2, 3) == pack('<qqh', 1, 2, 3)
True
>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
24
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
True

Приведенные выше результаты (выполненные на 64-битной машине) не гарантируют совпадения при выполнении на разных машинах. Например, приведенные ниже примеры были выполнены на 32-битной машине:

>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
12
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
False

Занятия

Модуль struct также определяет следующий тип:

class struct.Struct(format)

Возвращает новый объект Struct, который записывает и считывает двоичные данные в соответствии со строкой формата format. Однократное создание объекта Struct и вызов его методов более эффективны, чем вызов функций уровня модуля с тем же форматом, поскольку строка формата компилируется только один раз.

Примечание

Скомпилированные версии самых последних строк формата, переданных функциям уровня модуля, кэшируются, поэтому программам, использующим всего несколько строк формата, не нужно беспокоиться о повторном использовании одного экземпляра Struct.

Скомпилированные объекты Struct поддерживают следующие методы и атрибуты:

pack(v1, v2, ...)

Идентична функции pack(), используя скомпилированный формат. (len(result) будет равно size).

pack_into(buffer, offset, v1, v2, ...)

Идентична функции pack_into(), использует скомпилированный формат.

unpack(buffer)

Идентична функции unpack(), использует скомпилированный формат. Размер буфера в байтах должен быть равен size.

unpack_from(buffer, offset=0)

Идентична функции unpack_from(), использует скомпилированный формат. Размер буфера в байтах, начиная с позиции offset, должен быть не менее size.

iter_unpack(buffer)

Идентична функции iter_unpack(), использует скомпилированный формат. Размер буфера в байтах должен быть кратен size.

Added in version 3.4.

format

Строка формата, используемая для построения данного объекта Struct.

Изменено в версии 3.7: Тип строки формата теперь str вместо bytes.

size

Вычисленный размер структуры (и, следовательно, байтового объекта, созданного методом pack()), соответствующей format.

Изменено в версии 3.13: Изменилось значение repr() для структур. Теперь это:

>>> Struct('i')
Struct('i')