- Home »

Python struct pack и unpack — работа с бинарными данными
В этой статье разберёмся, что такое Python struct pack и unpack, зачем вообще возиться с бинарными данными, и как это может пригодиться в реальной жизни — особенно если вы не просто пишете скрипты на коленке, а занимаетесь настройкой серверов, автоматизацией, мониторингом или интеграцией с железом и сетями. Будет много практики, примеры из жизни, сравнения с альтернативами, а ещё — лайфхаки, которые реально экономят время. Если вы когда-нибудь сталкивались с задачей: «надо быстро распарсить бинарный лог, собрать пакет для железки, или просто сэкономить место при передаче данных» — этот пост для вас.
Что такое struct pack/unpack и зачем оно нужно?
Если коротко: struct — это стандартный модуль Python, который позволяет кодировать и декодировать данные в бинарном формате, строго по заданной схеме. Почему это важно? Потому что мир не ограничивается JSON и YAML. Иногда приходится работать с низкоуровневыми протоколами, бинарными логами, файлами, которые не открываются в текстовом редакторе, или даже с железом, где каждый байт на счету.
- Передача данных по сети (например, собственные протоколы, IoT, SNMP, Modbus, MQTT с кастомными payload’ами)
- Работа с бинарными файлами (логи, дампы, образы, firmware, протоколы обмена с железом)
- Интеграция с C/С++ библиотеками, где структура данных фиксирована
- Оптимизация хранения (например, если надо сэкономить место или ускорить парсинг)
В отличие от сериализации в pickle, JSON или XML, struct работает с байтами напрямую, без лишних метаданных и магии. Это быстро, компактно и предсказуемо. Но требует понимания, как устроены данные на низком уровне.
Как это работает? (простым языком, но по-взрослому)
Вся магия крутится вокруг двух функций: struct.pack() и struct.unpack(). Первая превращает ваши значения (int, float, bytes, char и т.д.) в бинарную строку (bytes), вторая — обратно. Всё это делается по формату, который вы задаёте строкой формата (format string).
- pack(fmt, v1, v2, …) — упаковывает значения в бинарный буфер по формату fmt
- unpack(fmt, buffer) — распаковывает бинарный буфер обратно в значения по формату fmt
Пример: Допустим, надо упаковать два целых числа (4 байта каждое, little-endian) и строку из 6 символов:
import struct
data = struct.pack('<ii6s', 42, 1337, b'python')
print(data) # b'*\x00\x00\x00)\x05\x00\x00python'
Здесь <ii6s
— это формат: <
— little-endian, i
— int (4 байта), 6s
— строка из 6 байт.
Распаковка:
unpacked = struct.unpack('<ii6s', data)
print(unpacked) # (42, 1337, b'python')
Ключевые моменты:
- Формат обязателен и должен точно соответствовать структуре данных
- Любая ошибка в формате — получите мусор или ошибку
- Работает быстро, без лишних зависимостей
Как быстро и просто всё настроить?
Если вы только начинаете, вот пошаговый чеклист:
- Убедитесь, что у вас Python 3.x (struct — стандартный модуль, ничего ставить не надо)
- Ознакомьтесь с официальной документацией (там есть таблица форматов)
- Определите, какой формат данных вам нужен (размеры, порядок байт, типы)
- Потренируйтесь на простых примерах (см. ниже)
- Проверьте, что ваши данные корректно упаковываются и распаковываются (тестируйте!)
Минимальный пример:
import struct
# Упаковать число 1000 (int) и число 3.14 (float) в big-endian
packed = struct.pack('>if', 1000, 3.14)
print(packed) # b'\x00\x00\x03\xe8@H\xf5\xc3'
# Распаковать обратно
unpacked = struct.unpack('>if', packed)
print(unpacked) # (1000, 3.140000104904175)
Совет: Если работаете с чужими бинарными файлами или протоколами — всегда сверяйтесь с документацией или спецификацией. Ошибка в одном байте — и всё поедет.
Примеры, схемы, практические советы
Таблица форматов struct
Код | Тип | Размер (байт) | Пример |
---|---|---|---|
b | signed char | 1 | -128..127 |
B | unsigned char | 1 | 0..255 |
h | short | 2 | -32768..32767 |
H | unsigned short | 2 | 0..65535 |
i | int | 4 | -2^31..2^31-1 |
I | unsigned int | 4 | 0..2^32-1 |
f | float | 4 | IEEE 754 |
d | double | 8 | IEEE 754 |
s | char[] (строка) | n | n-байтная строка |
Префиксы порядка байт:
<
— little-endian>
— big-endian=
— native (по умолчанию)!
— network (= big-endian)
Положительный кейс: парсинг бинарного лога
Допустим, у вас есть бинарный лог, где каждая запись — это 4 байта времени (uint32), 2 байта кода события (uint16), 1 байт флага (bool), и 8 байт данных (double). Как быстро распарсить?
import struct
record_fmt = '<IHBd' # uint32, uint16, uint8, double
record_size = struct.calcsize(record_fmt)
with open('log.bin', 'rb') as f:
while chunk := f.read(record_size):
timestamp, code, flag, value = struct.unpack(record_fmt, chunk)
print(timestamp, code, flag, value)
Рекомендация: Используйте struct.calcsize()
для автоматического вычисления размера записи.
Отрицательный кейс: неправильный формат
Частая ошибка — несоответствие формата и данных. Например, если вы попытаетесь распаковать 10 байт по формату, который требует 12 — получите ошибку:
struct.unpack('<IHBd', b'\x01\x00\x00\x00\x02\x00\x01\x00\x00\x00')
# struct.error: unpack requires a buffer of 15 bytes
Рекомендация: Всегда проверяйте размер буфера и формат. Для сложных структур — пишите тесты!
Сравнение с альтернативами
Решение | Плюсы | Минусы | Когда использовать |
---|---|---|---|
struct | Стандартный, быстрый, компактный, гибкий | Требует ручного описания формата, нет автогенерации | Бинарные протоколы, low-level, интеграция с C |
pickle | Просто, сериализует любые объекты Python | Не кросс-языковой, небезопасен, громоздкий | Внутренние задачи, не для передачи по сети |
json | Читаемый, кросс-языковой | Только текст, неэффективен для бинарных данных | API, web, обмен с людьми |
protobuf, msgpack | Кросс-языковые, автогенерация, компактные | Зависимости, сложнее, не всегда нужны | Большие проекты, микросервисы, API |
Команды и быстрые рецепты
Вот набор команд и приёмов, которые реально ускоряют работу:
# Проверить размер структуры
struct.calcsize('<IHBd')
# Упаковать массив чисел
struct.pack('<5i', *[1,2,3,4,5])
# Распаковать массив
struct.unpack('<5i', packed_data)
# Преобразовать IP-адрес в 4 байта
import socket
struct.pack('!4B', *map(int, '192.168.1.1'.split('.')))
# Распаковать обратно
'.'.join(map(str, struct.unpack('!4B', packed_ip)))
Похожие решения, программы и утилиты
- array — для массивов чисел, если не нужен сложный формат
- ctypes — для работы с C-структурами и библиотеками
- construct — мощная библиотека для описания сложных бинарных форматов (DSL для структур)
- protobuf — для кросс-языковой сериализации
- msgpack — бинарный аналог JSON
Статистика, сравнение с другими решениями
Скорость: struct работает практически на скорости C, так как реализован на C. Для простых операций (упаковка/распаковка десятков тысяч структур) — быстрее, чем любые сторонние библиотеки.
Размер данных: struct не добавляет никаких метаданных, только ваши байты. Для хранения и передачи это плюс. Для сложных структур — минус (нет автодокументации, надо помнить формат).
Кросс-языковость: struct — это Python-only, но если вы знаете формат, можно читать/писать такие данные и на C, Go, Rust, Java и т.д.
Интересные факты и нестандартные способы использования
- Можно использовать struct для генерации бинарных файлов конфигурации для железа (например, FPGA, микроконтроллеры, сетевые устройства)
- Можно быстро парсить бинарные дампы памяти или сетевые пакеты (например, для анализа трафика или reverse engineering)
- Можно делать «мини-архивы» — например, упаковать несколько файлов в один бинарный blob с заголовками
- Можно использовать struct для создания бинарных payload’ов для fuzzing’а (автоматического тестирования протоколов)
- Можно быстро сериализовать данные для передачи между процессами (например, через shared memory или сокеты)
Какие новые возможности открываются и чем это поможет в автоматизации и скриптах?
- Можно автоматизировать работу с железом, где требуется точный бинарный формат (например, SNMP traps, Modbus, custom TCP/UDP протоколы)
- Можно писать свои парсеры для нестандартных логов и дампов (например, если надо мониторить железо или сеть)
- Можно экономить место при хранении больших объёмов данных (например, если логи идут в терабайтах, а место на сервере не резиновое)
- Можно интегрироваться с C/С++ библиотеками без лишних прослоек (например, если надо быстро обмениваться данными между Python и C)
- Можно делать свои мини-протоколы для обмена между сервисами (например, если JSON слишком медленный или громоздкий)
Вывод — заключение и рекомендации
struct — это не просто очередная библиотека для сериализации. Это инструмент для тех, кто работает с низкоуровневыми данными, железом, бинарными протоколами и хочет держать всё под контролем. Если вы настраиваете серверы, пишете свои агенты мониторинга, интегрируетесь с железом или просто хотите ускорить обмен данными — обязательно попробуйте struct. Он прост, быстр, надёжен и не требует лишних зависимостей.
Рекомендации:
- Используйте struct для бинарных протоколов, логов, обмена с железом
- Для сложных структур — рассмотрите construct или protobuf
- Для хранения и передачи между Python-скриптами — struct быстрее и компактнее, чем pickle или JSON
- Всегда тестируйте форматы и проверяйте размеры буферов
- Не бойтесь экспериментировать — struct отлично подходит для автоматизации, мониторинга и интеграции
Если вы ищете быстрый и надёжный хостинг для своих проектов, обратите внимание на VPS или выделенные серверы — это даст вам полный контроль и свободу для экспериментов с Python и бинарными данными.
Полезные ссылки:
- Официальная документация Python struct
- construct — расширенный парсер бинарных структур
- Protocol Buffers
Если остались вопросы — смело пишите в комментарии, делитесь своими кейсами и лайфхаками!
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.