Документация Python неофициальный перевод
Содержание страницы

struct – Интерпретация байтов как упакованных двоичных данныхstruct – Interpret bytes as packed binary data

Исходный код: Lib/struct.py


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

Примечание

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

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

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

Модуль определяет следующие исключения и функции:

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)

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

struct.iter_unpack(format, buffer)

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

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

Добавлено в версии 3.4.

struct.calcsize(format)

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

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

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

Порядок байтов, размер и выравнивание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. Чтобы выровнять конец структуры по требованию выравнивания конкретного типа, завершите формат кодом этого типа с нулевым количеством повторений. См. Примеры.

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

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

Формат

Тип C

Тип Python

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

Примечания

x

байт заполнения

нет значения

(7)

c

char

строка байт длиной 1

1

b

signed char

int

1

(2)

B

unsigned char

int

1

(2)

?

_Bool

bool

1

(1)

h

short

int

2

(2)

H

unsigned short

int

2

(2)

i

int

int

4

(2)

I

unsigned int

int

4

(2)

l

long

int

4

(2)

L

unsigned long

int

4

(2)

q

long long

int

8

(2)

Q

unsigned long long

int

8

(2)

n

ssize_t

int

(2), (3)

N

size_t

int

(2), (3)

e

_Float16

float

2

(4), (6)

f

float

float

4

(4)

d

double

float

8

(4)

Zf

float complex

комплексное число

8

(10)

Zd

double complex

комплексное число

16

(10)

s

char[]

bytes

(9)

p

char[]

bytes

(8)

P

void*

int

(2), (5)

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

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

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

Изменено в версии 3.15: Добавлена поддержка форматов 'Zf' и 'Zd'. Форматы 'F' и 'D' являются мягко устаревшими.

См. также

Модули array и ctypes, а также сторонние модули, такие как numpy, используют похожие, но немного отличающиеся коды типов.

Примечания:

  1. Код преобразования '?' соответствует типу _Bool, определённому стандартами C начиная с C99. В стандартном режиме он представляется одним байтом.

  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. Он имеет знаковый бит, 5-битную экспоненту и 11-битную точность (с 10 явно хранимыми битами), и может представлять числа от примерно 6.1e-05 до 6.5e+04 с полной точностью. Этот тип не широко поддерживается компиляторами C: он доступен как тип _Float16, если компилятор поддерживает Приложение H стандарта C23. На типичной машине для хранения можно использовать unsigned short, но не для математических операций. См. страницу Википедии о формате половинной точности с плавающей запятой для получения дополнительной информации.

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

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

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

  10. Для кодов типа 'Zf' и 'Zd' упакованное представление использует форматы IEEE 754 binary32 и binary64 для компонентов комплексного числа, независимо от формата чисел с плавающей запятой, используемого платформой. Обратите внимание, что комплексные типы доступны безусловно, несмотря на то, что комплексные типы являются опциональной возможностью в C. Как указано в стандарте C11, каждый комплексный тип представляется массивом C из двух элементов, содержащим соответственно действительную и мнимую части. Символы формата 'F' и 'D' (для 'Zf' и 'Zd' соответственно) поддерживаются для совместимости.

Символу формата может предшествовать целочисленный счётчик повторений. Например, строка формата '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 при распаковке.

ПримерыExamples

Примечание

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

Упакуйте и распакуйте целые числа трёх разных размеров, используя порядок 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 ниже после упаковки '#' были добавлены три нулевых байта, чтобы выровнять следующее целое число по границе четырёх байт. В этом примере вывод был получен на машине с порядком от младшего к старшему:

>>> 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' приводит к добавлению двух байт выравнивания в конце, если предполагается, что длинные целые платформы выровнены по 4-байтовым границам:

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

См. также

Модуль array

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

Модуль json

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

Модуль pickle

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

ПримененияApplications

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

Собственные форматыNative Formats

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

Рассмотрим два простых примера (на 64-битной машине с порядком от младшего к старшему):

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

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

>>> calcsize('@llh0l')
24

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

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

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

При обмене данными за пределами процесса, например по сети или при хранении, будьте точны. Укажите точный порядок байт, размер и выравнивание. Не предполагайте, что они совпадают с собственным порядком конкретной машины. Например, сетевой порядок байт – от старшего к младшему, в то время как многие популярные ЦП используют порядок от младшего к старшему. Явно определяя это, пользователю не нужно заботиться об особенностях платформы, на которой выполняется его код. Первым символом обычно должен быть < или > (или !). Выравнивание – ответственность программиста. Код формата с нулевым повторением не работает. Вместо этого пользователь должен явно добавлять байты выравнивания '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

КлассыClasses

Модуль 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.

Добавлено в версии 3.4.

format

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

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

size

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

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

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