- POST
- struct.pack()
- struct.unpack()
- struct.calcsize()
- Пакуем байты на Python: struct
- Little-endian и big-endian
- Таблица типов данных
- О форматной строке
- Полезности
- Python struct pack, unpack
- Python Struct
- Python Struct Functions
- Python struct.pack()
- Python struct.unpack()
- Python struct calcsize()
- Python struct pack_into(), unpack_from()
POST
Python uses struct.pack() to pack python data type into binary data (Byte Stream), which means convert a value to a string based on the format.
Because there is no byte type in Python, where the string is equivalent to a byte stream, or byte array.
struct.pack()
struck.pack(format, data1, data2. )
format: define the converted format. data1,data2,…: the data needs to be packed.
Format | C Type | Python Type | Standard Size |
---|---|---|---|
x | pad byte | ||
c | char | string of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
q | long long | integer | 8 |
Q | unsigned long long | integer | 8 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | string | |
p | char[]string | ||
P | void * | integer |
To exchange data with the structure in C, it is also necessary to consider that some C or C + + compilers use byte alignment, usually a 32-bit system with 4 bytes as the unit. Therefore, a struct can be converted according to the byte order of the local machine. The alignment can be changed by the first character in the format. The definition is as follows:
Format | Byte Order | Size | alignment |
---|---|---|---|
@ | native byte | native | native |
= | native | standard | none |
little-endian char | standard | none | |
> | big-endian char | standard | none |
! | network (= big-endian) | standard | none |
Use it in the first place of format.
import struct a = 11 b = 12 print(len(struct.pack("ii",a,b))) 8 print(struct.pack("ii",a,b)) b'\x0b\x00\x00\x00\x0c\x00\x00\x00'
struct.unpack()
struct.unpack() unpacks the byte stream into Python data type.
The function prototype:
This function return a tuple.
a = 11 b = 12 packdata = struct.pack("ii",a,b) c,d = struct.unpack("1i1i",packdata) print((c,d)) (11,12)
struct.calcsize()
Struct.calcsize() is used to calculate the length of the result corresponding to the format string.
print(struct.calcsize("c")) 1 print(struct.calcsize("H")) 2 print(struct.calcsize("L")) 4 print(struct.calcsize("Q")) 8
This article introduced the Python struct.pack() method.
Пакуем байты на Python: struct
Кто сказал, что нельзя делать низкоуровневые вещи на Python? Конечно, можно. Давайте научимся упаковывать данные из Python в байты и распаковывать их обратно.
Встроенный модуль struct как раз создан для этих целей. В низкоуровневом деле важны детали, а именно размер каждого элемента данных, их порядок в структуре, а также порядок байт для многобайтовых типов данных. Для определения этих деталей модуль struct вводит форматные строки (не путать с str.format, там другой формат).
Начнем с простого примера:
>>> import struct >>> struct.pack("hhl", 1, 2, 3) b'\x01\x00\x02\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'
Здесь происходит вот что. Мы берем три числа: 1, 2, 3 и пакуем их в байты, таким образом, что первое и второе числа трактуются как тип short int (4 байта на моей машине), а последнее, как long int (8 байт на моей машине). Это типы не из Python, а из языка Си. Ознакомьтесь с типами языка Си, если хотите понимать, что они из себя представляют и какой размер в байтах имеют.
Обратная распаковка байт в кортеж значений по заданному формату:
>>> struct.unpack("hhl", b'\x01\x00\x02\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00') (1, 2, 3)
Форматы запаковки и распаковки должны совпадать, иначе данные будут неправильно интерпретированы или испорчены, или же вообще возникнет ошибка из-за того, что размер данных не подходит под ожидаемый формат (struct.error):
>>> struct.unpack("hhl", b'\x01\x02\x03') Traceback (most recent call last): File "", line 1, in struct.error: unpack requires a buffer of 16 bytes
Обратите внимание, что я выше писал, что размер элемента «h» – 4 байта именно на моей машине. Может статься так, что на машине с другим процессором, архитектурой или просто с другой версией ОС размер типа будет другой. Для 32 битных систем, это обычно будет 2 байта.
Но, что если данных передаются по сети или через носители информации между системами с разной или неизвестной заранее архитектурой? Конечно, у struct есть средства на такие случаи. Первый символ форматной строки обозначит порядок байт. Обратите внимание на таблицу:
Символ | Порядок байт | Размеры типов | Выравнивание |
---|---|---|---|
@ | нативный | нативный | нативное |
= | нативный | стандартные | нет |
little-endian | стандартные | нет | |
> | big-endian | стандартные | нет |
! | сетевой (= big-endian) | стандартные | нет |
Нативный – значит родной для конкретно вашей машины и системы. По умолчанию порядок байт и размер типов данных как раз нативный (символ @).
Стандартный размер – размер, который фиксирован стандартом и не зависит от текущей платформы. Например, char всегда 1 байт, а int – 4 байта. Если мы планируем распространять запакованные байты, мы должны гарантировать, что размер типов будет всегда стандартный. Для этого подходит любой из символов «=«, ««, «>«, «!» в начале форматной строки.
Little-endian и big-endian
Little-endian и big-endian – это два основных порядка байт. Представим, что у нас есть короткое целое (short int), и оно занимает два (2) байта. Какой из байтов должен идти сначала, а какой в конце?
В big-endian порядок от старшего байта к младшему. В little-endian порядок от младшего байта к старшему. Как узнать на Python какой порядок байт в системе:
>>> import sys >>> sys.byteorder 'little'
Давайте наглядно посмотрим как пакуются байты при разных порядках. Для числа 258 в форме short младший байт будет = 2, а старший = 1:
>>> struct.pack(">> struct.pack(">h", 258) # big-endian b'\x01\x02'
Как видите порядок байт противоположный для разных случаев.
В сетевых протоколах принято использовать big-endian (символ «!» – псевдоним к «>«), а на большинстве современных настольных систем используется little-endian.
Таблица типов данных
Теперь ознакомимся с таблицей типов данных, которая дает соответствие символу форматной строки (код преобразования) с Си-типом данных, Python-типом данных и стандартный размером. Еще раз: стандартный размер будет только, если задан первый символ как ««, «>«, «!» или «=«. Для «@» или по умолчанию – размер данных определяется текущей системой (платформо-зависимо).
Символ | Тип в языке Си | Python тип | Станд. размер |
---|---|---|---|
x | байт набивки | нет значения | |
c | char | bytes длины 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
q | long long | integer | 8 |
Q | unsigned long long | integer | 8 |
n | ssize_t | integer | зависит |
N | size_t | integer | зависит |
e | «половинный float« | float | 2 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | bytes | указывается явно |
p | char[] — строка из Паскаля | bytes | указывается явно |
Коды «e«, «f«, «d» используют бинарный формат IEEE-754.
Код «x» это просто байт набивки. Он не попадает в распакованные данные, а нужен, чтобы выравнивать данные. «x» при запаковке забиваются пустыми байтами. Пример: «пусто-число-пусто-пусто-число-пусто»:
>>> struct.pack(">xBxxBx", 255, 128) b'\x00\xff\x00\x00\x80\x00' >>> struct.unpack('>xBxxBx', b'\x00\xff\x00\x00\x80\x00') (255, 128)
О форматной строке
Если в форматной строке перед символом кода – число, то значит этот символ повторяется столько раз, сколько указывает число. Два кусочка кода аналогичны:
>>> struct.pack(">3h", 1, 2, 3) b'\x00\x01\x00\x02\x00\x03' >>> struct.pack(">hhh", 1, 2, 3) b'\x00\x01\x00\x02\x00\x03'
Для строк (коды «s» и «p«) надо указывать число байт – длину строки, иначе будет считаться 1 байт:
>>> struct.pack("ss", b"abc", b"XYZW") # не указал длину - потерял байты b'aX' >>> struct.pack("3s4s", b"abc", b"XYZW") b'abcXYZW'
10s – одна 10-символьная строка, а 10c – 10 отдельных символов:
>>> struct.unpack('10c', b'abracadabr') (b'a', b'b', b'r', b'a', b'c', b'a', b'd', b'a', b'b', b'r') >>> struct.unpack('10s', b'abracadabr') (b'abracadabr',)
Можно вставлять пробелы между отдельными элементами форматной строки (но нельзя отделать число от символа). Пробелы игнорируются при чтении строки и нужны для удобства чтения кода программистом:
>>> struct.pack('>6sh?', b'python', 65, True) b'python\x00A\x01' >>> struct.pack('> 6s h ?', b'python', 65, True) # тоже, но с пробелами b'python\x00A\x01' >>> struct.unpack('> 6s h ?', b'python\x00A\x01') (b'python', 65, True)
Полезности
Можно вычислить размер данных из форматной строки без фактической запаковки или распаковки данных:
Удобно распаковывать байты прямо в именованные кортежи:
>>> from collections import namedtuple >>> Student = namedtuple('Student', 'name serialnum school gradelevel') >>> record = b'raymond \x32\x12\x08\x01\x08' >>> Student._make(struct.unpack('Запаковка в буффер со смещением struct.pack_into(format, buffer, offset, v1, v2, . ) :
>>> buffer = bytearray(40) >>> struct.pack_into('h l', buffer, 10, 3432, 340840) >>> buffer bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00h\r\x00\x00\x00\x00\x00\x00h3\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') >>>Распаковка из буффера со смещением:
>>> x, y = struct.unpack_from('h l', buffer, 10) >>> x, y (3432, 340840)Распаковка нескольких однотипных структур:
>>> chunks = struct.pack('hh', 10, 20) * 5 >>> chunks # 5 одинаковых штук b'\n\x00\x14\x00\n\x00\x14\x00\n\x00\x14\x00\n\x00\x14\x00\n\x00\x14\x00' >>> [(x, y) for x, y in struct.iter_unpack('hh', chunks)] [(10, 20), (10, 20), (10, 20), (10, 20), (10, 20)]Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈
Python struct pack, unpack
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
Python struct module is capable of performing the conversions between the Python values and C structs, which are represented as Python Strings.
Python Struct
- Python struct module can be used in handling binary data stored in files, database or from network connections etc.
- It uses format Strings as compact descriptions of the layout of the C structs and the intended conversion to/from Python values.
Python Struct Functions
There are five important functions in struct module - pack() , unpack() , calcsize() , pack_into() and unpack_from() . In all these functions, we have to provide the format of the data to be converted into binary. Some of the popular format characters are:
?: boolean h: short l: long i: int f: float q: long long int
You can get the complete list of format characters here. Let’s start looking into struct module functions one by one.
Python struct.pack()
This function packs a list of values into a String representation of the specified type. The arguments must match the values required by the format exactly. Let’s quickly look at struct pack() example:
import struct var = struct.pack('hhl', 5, 10, 15) print(var) var = struct.pack('iii', 10, 20, 30) print(var)
When we run this script, we get the following representation: Note that ‘b’ in the Output stands for binary.
Python struct.unpack()
This function unpacks the packed value into its original representation with the specified format. This function always returns a tuple, even if there is only one element. Let’s quickly look at struct unpack() function example:
import struct var = struct.pack('hhl', 5, 10, 15) print(var) print(struct.unpack('hhl', var))
When we run this script, we get back our original representation: Clearly, we must tell the Python interpreter the format we need to unpack the values into.
Python struct calcsize()
This function calculates and returns the size of the String representation of struct with a given format. Size is calculated in terms of bytes. Let’s quickly look at an example code snippet:
import struct var = struct.pack('hhl', 5, 10, 15) print(var) print("Size of String representation is <>.".format(struct.calcsize('hhl')))
When we run this script, we get the following representation:
Python struct pack_into(), unpack_from()
These functions allow us to pack the values into string buffer and unpack from a string buffer. These functions are introduced in version 2.5.
import struct # ctypes is imported to create a string buffer import ctypes # As shown in previous example size = struct.calcsize('hhl') print(size) # Buffer 'buff' is created from ctypes buff = ctypes.create_string_buffer(siz) # struct.pack_into() packs data into buff and it doesn't return any value # struct.unpack_from() unpacks data from buff, returns a tuple of values print(struct.pack_into('hhl', buff, 0, 5, 10, 15)) print(struct.unpack_from('hhl', buff, 0))
When we run this script, we get the following representation: That’s all for a short introduction of python struct module.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.