Home » Python struct pack и unpack — работа с бинарными данными
Python struct pack и unpack — работа с бинарными данными

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')

Ключевые моменты:

  • Формат обязателен и должен точно соответствовать структуре данных
  • Любая ошибка в формате — получите мусор или ошибку
  • Работает быстро, без лишних зависимостей

Как быстро и просто всё настроить?

Если вы только начинаете, вот пошаговый чеклист:

  1. Убедитесь, что у вас Python 3.x (struct — стандартный модуль, ничего ставить не надо)
  2. Ознакомьтесь с официальной документацией (там есть таблица форматов)
  3. Определите, какой формат данных вам нужен (размеры, порядок байт, типы)
  4. Потренируйтесь на простых примерах (см. ниже)
  5. Проверьте, что ваши данные корректно упаковываются и распаковываются (тестируйте!)

Минимальный пример:


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 и бинарными данными.

Полезные ссылки:

Если остались вопросы — смело пишите в комментарии, делитесь своими кейсами и лайфхаками!


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

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

Leave a reply

Your email address will not be published. Required fields are marked