The struct module converts between Python values and C-style binary data. Essential for binary protocols, file formats, and network communication.
Basic Pack/Unpack
import struct
# Pack values into bytes
data = struct.pack('ihf', 1, 2, 3.14)
print(data) # b'\x01\x00\x00\x00\x02\x00\x9a\x99I@'
# Unpack bytes to values
values = struct.unpack('ihf', data)
print(values) # (1, 2, 3.140000104904175)Format Characters
| Char | C Type | Python | Size |
|---|---|---|---|
b | signed char | int | 1 |
B | unsigned char | int | 1 |
h | short | int | 2 |
H | unsigned short | int | 2 |
i | int | int | 4 |
I | unsigned int | int | 4 |
q | long long | int | 8 |
Q | unsigned long long | int | 8 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | bytes | n |
? | _Bool | bool | 1 |
Byte Order
import struct
value = 0x12345678
# Native byte order (default)
struct.pack('I', value)
# Little-endian
struct.pack('<I', value) # b'xV4\x12'
# Big-endian (network order)
struct.pack('>I', value) # b'\x124Vx'
# Network order (big-endian)
struct.pack('!I', value) # Same as >Struct Class (Performance)
import struct
# Compile format once
header_format = struct.Struct('>BBHI')
# Reuse for multiple operations
header = header_format.pack(1, 2, 3, 4)
values = header_format.unpack(header)
print(header_format.size) # 8 bytesPacking Strings
import struct
# Fixed-length string
name = b'Alice'
packed = struct.pack('10s', name) # Padded with nulls
print(packed) # b'Alice\x00\x00\x00\x00\x00'
# Unpack
unpacked = struct.unpack('10s', packed)
print(unpacked[0].rstrip(b'\x00')) # b'Alice'Arrays of Values
import struct
# Pack multiple integers
numbers = [1, 2, 3, 4, 5]
packed = struct.pack(f'{len(numbers)}i', *numbers)
# Unpack
unpacked = struct.unpack(f'{len(numbers)}i', packed)
print(unpacked) # (1, 2, 3, 4, 5)Reading Binary Files
import struct
# BMP file header
with open('image.bmp', 'rb') as f:
# Read 14-byte BMP header
header = f.read(14)
signature, size, _, _, offset = struct.unpack('<2sIHHI', header)
print(f"Signature: {signature}")
print(f"File size: {size}")
print(f"Data offset: {offset}")Writing Binary Files
import struct
def write_wav_header(f, channels, sample_rate, bits_per_sample, data_size):
byte_rate = sample_rate * channels * bits_per_sample // 8
block_align = channels * bits_per_sample // 8
# RIFF header
f.write(struct.pack('<4sI4s', b'RIFF', 36 + data_size, b'WAVE'))
# fmt chunk
f.write(struct.pack('<4sIHHIIHH',
b'fmt ', 16, 1, channels, sample_rate,
byte_rate, block_align, bits_per_sample))
# data chunk header
f.write(struct.pack('<4sI', b'data', data_size))Network Protocols
import struct
import socket
def create_icmp_packet(icmp_type, code, checksum, identifier, sequence, data):
"""Create ICMP packet."""
return struct.pack(
'>BBHHH',
icmp_type,
code,
checksum,
identifier,
sequence
) + data
def parse_ip_header(packet):
"""Parse IPv4 header."""
header = struct.unpack('>BBHHHBBHII', packet[:20])
return {
'version': header[0] >> 4,
'ihl': header[0] & 0xF,
'ttl': header[5],
'protocol': header[6],
'src': socket.inet_ntoa(struct.pack('>I', header[8])),
'dst': socket.inet_ntoa(struct.pack('>I', header[9])),
}unpack_from / pack_into
import struct
# Unpack from offset
data = b'\x00\x00\x01\x00\x02\x00\x03\x00'
values = struct.unpack_from('>HHH', data, offset=2)
print(values) # (1, 2, 3)
# Pack into buffer
buffer = bytearray(10)
struct.pack_into('>HH', buffer, 2, 100, 200)
print(buffer) # bytearray(b'\x00\x00\x00d\x00\xc8\x00\x00\x00\x00')calcsize
import struct
# Get packed size
print(struct.calcsize('i')) # 4
print(struct.calcsize('10s')) # 10
print(struct.calcsize('>BBHI')) # 8iter_unpack
import struct
# Unpack repeated structures
data = struct.pack('>HH' * 3, 1, 2, 3, 4, 5, 6)
for x, y in struct.iter_unpack('>HH', data):
print(f"({x}, {y})")
# (1, 2)
# (3, 4)
# (5, 6)Practical: Binary Record
import struct
from dataclasses import dataclass
@dataclass
class Record:
id: int
timestamp: int
value: float
name: bytes
FORMAT = '>IQd20s'
SIZE = struct.calcsize(FORMAT)
def pack(self):
name = self.name.ljust(20, b'\x00')[:20]
return struct.pack(self.FORMAT, self.id, self.timestamp, self.value, name)
@classmethod
def unpack(cls, data):
id, ts, val, name = struct.unpack(cls.FORMAT, data)
return cls(id, ts, val, name.rstrip(b'\x00'))
# Usage
record = Record(1, 1711042200, 3.14, b'test')
packed = record.pack()
restored = Record.unpack(packed)Bit Fields
import struct
def pack_flags(flag1, flag2, flag3, value):
"""Pack flags into single byte."""
flags = (flag1 << 7) | (flag2 << 6) | (flag3 << 5) | (value & 0x1F)
return struct.pack('B', flags)
def unpack_flags(data):
flags = struct.unpack('B', data)[0]
return {
'flag1': bool(flags & 0x80),
'flag2': bool(flags & 0x40),
'flag3': bool(flags & 0x20),
'value': flags & 0x1F,
}Padding and Alignment
import struct
# Native alignment includes padding
struct.calcsize('bI') # May be 8 (3 bytes padding)
struct.calcsize('=bI') # 5 (no padding, native byte order)
# Explicit padding
struct.pack('b3xI', 1, 2) # 1 byte + 3 padding + 4 byte intSummary
struct module essentials:
- pack/unpack: Convert values ↔ bytes
- Byte order:
<little,>big,!network - Struct class: Compile format for reuse
- iter_unpack: Process repeated structures
- pack_into/unpack_from: Work with buffers
Critical for binary protocols, file formats, and network programming.
React to this post: