struct — Интерпретация байтов как упакованных двоичных данных
Исходный код: Lib/struct.py
Этот модуль преобразует значения Python в структуры C, представленные в виде объектов Python bytes. Компактные строки форматов описывают предполагаемые преобразования между значениями Python. Функции и объекты модуля могут использоваться для двух основных задач: обмен данными с внешними источниками (файлами или сетевыми подключениями) или передача данных между приложением Python и слоем C.
Примечание
При отсутствии префиксного символа используется родной режим по умолчанию. Он упаковывает или распаковывает данные, основываясь на платформе и компиляторе, на которых был построен интерпретатор Python. Результат упаковки заданной структуры C включает в себя байты заполнения, которые поддерживают правильное выравнивание для задействованных типов C; аналогично, при распаковке учитывается выравнивание. В отличие от этого, при обмене данными между внешними источниками программист отвечает за определение порядка байтов и заполнения между элементами. Подробности см. в разделе Порядок байтов, размер и выравнивание.
Несколько struct функций (и методы Struct) принимают аргумент buffer. Это относится к объектам, которые реализуют протокол буферизации и предоставляют либо читаемый, либо читаемо-записываемый буфер. Наиболее распространённые типы, используемые для этой цели, — 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) -
Распаковывает данные из buffer, начиная с позиции offset, в соответствии со строкой формата format. Результатом является кортеж, даже если он содержит ровно один элемент. Размер буфера в байтах, начиная с позиции offset, должен быть не меньше размера, требуемого форматом, как указано функцией
calcsize().
-
struct.iter_unpack(format, buffer) -
Итеративно распаковывает данные из буфера buffer в соответствии со строкой формата format. Эта функция возвращает итератор, который будет читать блоки равного размера из буфера, пока все его содержимое не будет израсходовано. Размер буфера в байтах должен быть кратен размеру, требуемому форматом, как указано функцией
calcsize().Каждая итерация возвращает кортеж, как указано в строке формата.
Добавлена в версии 3.4.
-
struct.calcsize(format) -
Возвращает размер структуры (и, следовательно, объекта bytes, полученного функцией
pack(format, ...)) соответствующей строке формата format.
Строки форматов
Строки форматов описывают расположение данных при упаковке и распаковке. Они строятся из символов формата, которые указывают тип данных, которые упаковываются/распаковываются. Кроме того, специальные символы управляют порядком байтов, размером и выравниванием. Каждая строка формата состоит из необязательного префиксного символа, который описывает общие свойства данных, и одного или нескольких символов формата, которые описывают фактические значения данных и заполнение.
Порядок байтов, размер и выравнивание
По умолчанию типы C представляются в родном формате машины и порядке байтов и правильно выравниваются путем пропуска байтов заполнения при необходимости (в соответствии с правилами, используемыми компилятором C). Это поведение выбрано для того, чтобы байты упакованной структуры точно соответствовали расположению в памяти соответствующей структуры C. Выбор использования родного порядка байтов и заполнения или стандартных форматов зависит от приложения.
В качестве альтернативы, первый символ строки формата может использоваться для указания порядка байтов, размера и выравнивания упакованных данных в соответствии со следующей таблицей:
Символ | Порядок байтов | Размер | Выравнивание |
|---|---|---|---|
| родной | родной | родной |
| родной | стандартный | отсутствует |
| маленький конец | стандартный | отсутствует |
| большой конец | стандартный | отсутствует |
| сетевой (= большой конец) | стандартный | отсутствует |
Если первый символ не является одним из этих, предполагается '@'.
Примечание
Число 1023 (0x3ff в шестнадцатеричном виде) имеет следующие представления в байтах:
-
03 ffв формате с большим концом (>) -
ff 03в формате с маленьким концом (<)
Пример на Python:
>>> import struct
>>> struct.pack('>h', 1023)
b'\x03\xff'
>>> struct.pack('<h', 1023)
b'\xff\x03'
Порядок байтов по умолчанию — большой или малый конец, в зависимости от системы. Например, Intel x86, AMD64 (x86-64) и Apple M1 — малый конец; IBM z и многие устаревшие архитектуры — большой конец. Используйте sys.byteorder для проверки порядка байтов вашей системы.
Родной размер и выравнивание определяются с помощью выражения sizeof компилятора C. Это всегда комбинируется с родным порядком байтов.
Стандартный размер зависит только от символа формата; см. таблицу в разделе Символы формата.
Обратите внимание на разницу между '@' и '=': оба используют родной порядок байтов, но размер и выравнивание последнего стандартизированы.
Формат '!' представляет собой сетевой порядок байтов, который всегда является большим концом, как определено в IETF RFC 1700.
Нет способа указать неродной порядок байтов (вынужденный обмен байтами); используйте соответствующий выбор '<' или '>'.
Примечания:
- Заполнение добавляется только автоматически между последовательными членами структуры. Заполнение не добавляется в начале или конце закодированной структуры.
- Заполнение не добавляется при использовании неродного размера и выравнивания, например, с помощью ‘<’, ‘>’, ‘=’, и ‘!’.
- Чтобы выровнять конец структуры по требованиям выравнивания определенного типа, закончите формат кодом для этого типа с повторяемостью нуля. См. Примеры.
Символы форматирования
Символы форматирования имеют следующее значение; преобразование между значениями C и Python должно быть очевидным, учитывая их типы. Столбец «Стандартный размер» относится к размеру упакованного значения в байтах при использовании стандартного размера; то есть, когда строка форматирования начинается с одного из '<', '>', '!' или '='. При использовании размера по умолчанию размер упакованного значения зависит от платформы.
Формат | Тип C | Тип Python | Стандартный размер | Примечания |
|---|---|---|---|---|
| байт заполнения | нет значения | (7) | |
| char | байты длиной 1 | 1 | |
| signed char | целое число | 1 | (1), (2) |
| unsigned char | целое число | 1 | (2) |
| _Bool | bool | 1 | (1) |
| short | целое число | 2 | (2) |
| unsigned short | целое число | 2 | (2) |
| int | целое число | 4 | (2) |
| unsigned int | целое число | 4 | (2) |
| long | целое число | 4 | (2) |
| unsigned long | целое число | 4 | (2) |
| long long | целое число | 8 | (2) |
| unsigned long long | целое число | 8 | (2) |
|
| целое число | (3) | |
|
| целое число | (3) | |
| (6) | float | 2 | (4) |
| float | float | 4 | (4) |
| double | float | 8 | (4) |
| char[] | байты | (9) | |
| char[] | байты | (8) | |
| void* | целое число | (5) |
Изменено в версии 3.3: Добавлена поддержка форматов 'n' и 'N'.
Изменено в версии 3.6: Добавлена поддержка формата 'e'.
Примечания:
- Код преобразования
'?'соответствует типу _Bool, определенному стандартами C с C99. В стандартном режиме он представлен одним байтом. -
При попытке упаковать значение, которое не является целым числом, используя любой из кодов преобразования целых чисел, если для этого значения определен метод
__index__(), то этот метод вызывается для преобразования аргумента в целое число перед упаковкой.Изменено в версии 3.2: Добавлена поддержка метода
__index__()для значений, которые не являются целыми числами. - Символы преобразования
'n'и'N'доступны только для размера по умолчанию (выбранного по умолчанию или с символом порядка байтов'@'). Для стандартного размера можно использовать любой другой формат целых чисел, который подходит вашему приложению. - Для символов преобразования
'f','d'и'e', упакованное представление использует формат IEEE 754 binary32, binary64 или binary16 (соответственно для'f','d'или'e'), независимо от используемого на платформе формата с плавающей точкой. - Символ формата
'P'доступен только для естественного порядка байтов (выбранного по умолчанию или с символом порядка байтов'@'). Символ порядка байтов'='выбирает порядок little-endian или big-endian в зависимости от хост-системы. Модуль struct не интерпретирует это как естественный порядок, поэтому формат'P'недоступен. - Тип IEEE 754 binary16 «полуточная точность» был введен в пересмотре стандарта IEEE 754 2008 года (IEEE 754 standard). Он имеет бит знака, 5-битный экспоненту и 11-битную точность (с 10 битами, явно сохраненными), и может представлять числа приблизительно от
6.1e-05до6.5e+04с полной точностью. Этот тип не широко поддерживается компиляторами C: на типичном компьютере для хранения может использоваться unsigned short, но не для математических операций. Подробнее см. страницу Википедии о формате с плавающей точкой half-precision floating-point format. - При упаковке
'x'вставляет один байт NUL. - Символ формата
'p'кодирует «строку Паскаля», то есть короткую строку переменной длины, хранящуюся в фиксированном количестве байт, заданном счетчиком. Первый сохраненный байт — это длина строки или 255, что меньше. За ним следуют байты строки. Если строка, переданная вpack(), слишком длинная (длиннее, чем счетчик минус 1), хранятся только первыеcount-1байтов строки. Если строка корочеcount-1, она дополняется нулевыми байтами до тех пор, пока не будут использованы ровно count байтов. Обратите внимание, что дляunpack()символ формата'p'потребляетcountбайта, но возвращаемая строка никогда не может содержать более 255 байтов. - Для символа формата
's'значение count интерпретируется как длина байтов, а не как повторяющийся счетчик, как для других символов формата; например,'10s'означает одну строку длиной 10 байт, которая отображается или преобразуется в одну строку Python байтов, а'10c'означает 10 отдельных однобайтовых элементов символов (например,cccccccccc) отображающихся или преобразующихся в десять различных объектов байтов Python. (Примеры конкретной демонстрации разницы см. в разделе Примеры). Если count не указан, он по умолчанию равен 1. При упаковке строка усекается или дополняется нулевыми байтами в соответствии с потребностями, чтобы уместиться. При распаковке полученный объект байтов всегда имеет ровно указанное количество байтов. В качестве специального случая'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 вызов ниже, что три нулевых байта были добавлены после упакованного '#' для выравнивания следующего целого числа на границе в четыре байта. В этом примере вывод был получен на машине с little-endian порядком:
>>> 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'
Применение
Существуют два основных применения модуля struct: обмен данными между кодом Python и C в приложении или другом приложении, скомпилированном с использованием того же компилятора (родные форматы), и обмен данными между приложениями с использованием согласованного расположения данных (стандартные форматы). Как правило, строки формата, созданные для этих двух областей, различаются.
Родные форматы
При создании строк формата, имитирующих расположение исходного кода, порядок байтов и выравнивание определяются компилятором и архитектурой машины. В таких случаях следует использовать символ формата @ для указания родного порядка байтов и размеров данных. Внутренние байты заполнения обычно вставляются автоматически. Возможно, в конце строки формата потребуется код с нулевым повтором, чтобы округлить до правильной границы байтов для правильного выравнивания последовательных блоков данных.
Рассмотрим эти два простых примера (на 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, который записывает и считывает двоичные данные в соответствии со строкой формата формат. Создание объекта
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(), использующей скомпилированный формат. Размер буфера в байтах, начиная с позиции смещение, должен быть не меньшеsize.
-
iter_unpack(buffer) -
Идентично функции
iter_unpack(), использующей скомпилированный формат. Размер буфера в байтах должен быть кратенsize.Добавлен в версии 3.4.
-
format -
Строка формата, используемая для создания этого объекта Struct.
-
size -
Вычисленный размер структуры (и, следовательно, размер объекта bytes, полученного методом
pack()), соответствующийformat.
Изменено в версии 3.13: Представление структур в repr() изменилось. Теперь:
>>> Struct('i') Struct('i') -
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/struct.html