construct Binary Data Parser
construct is a powerful declarative symmetric parser/builder for binary data, supporting Python 3.6+. It allows you to define the structure of binary data using Python objects and then parse or build data according to that structure. The current version is 2.10.70, and it maintains a fairly active release cadence, often releasing minor fixes and improvements.
Warnings
- breaking Major API overhaul occurred between construct 2.0 and 2.5/2.6. This was a near-complete rewrite, deprecating and removing many constructs (e.g., `Buffer`, `BitStruct`), changing import paths (`construct.core`), and modifying core behaviors. Code written for construct 2.0 is highly unlikely to work with 2.5+.
- breaking Breaking changes were introduced in versions 2.8 and 2.9. Notably, `Subconstruct` was removed in 2.8 (use `Construct` directly), `RepeatUntil` was renamed to `GreedyRange`, and `Context.error_when` behavior changed slightly in 2.9.
- gotcha Construct objects (e.g., `Struct`, `Array`) are often mutable. If you create an instance of a construct and then modify its internal properties or reuse it in different contexts without care, you might encounter unexpected side effects due to shared state.
- gotcha The `this` (or `ctx`) variable, used for accessing context and previously parsed fields within `lambda` functions or methods, can be a source of confusion. Misunderstanding its scope or when certain fields become available can lead to errors.
Install
-
pip install construct
Imports
- Struct
from construct import Struct
- Int8ub
from construct import Int8ub
- Bytes
from construct import Bytes
- Array
from construct import Array
- GreedyRange
from construct import GreedyRange
- StreamError
from construct import StreamError
Quickstart
from construct import Struct, Int8ub, Bytes, Array, GreedyRange, StreamError
# Define a simple binary structure
# A header, an array of 8-bit unsigned integers, and a variable-length data block
packet_struct = Struct(
"header" / Bytes(4), # 4-byte header
"count" / Int8ub, # 1-byte count of following integers
"data" / Array(lambda ctx: ctx.count, Int8ub), # Array of 'count' integers
"payload" / GreedyRange(Int8ub) # Remaining bytes as a list of integers
)
# Example data to build
my_data = {
"header": b"\xde\xad\xbe\xef",
"count": 3,
"data": [10, 20, 30],
"payload": [1, 2, 3, 4, 5]
}
# Build binary data from Python object
built_binary = packet_struct.build(my_data)
print(f"Built binary: {built_binary.hex()}")
# Parse binary data into Python object
# Using a different example binary for parsing
binary_to_parse = b"\xaa\xbb\xcc\xdd\x02\x01\x02\xff\xee\xdd"
try:
parsed_object = packet_struct.parse(binary_to_parse)
print(f"Parsed object: {parsed_object}")
# Accessing fields
print(f"Parsed header: {parsed_object.header.hex()}")
print(f"Parsed data: {parsed_object.data}")
except StreamError as e:
print(f"Error parsing data: {e}")