Advanced

Packing values into bytes

A format says how many bytes each field takes and in what order. Packing writes values into those slots, and unpacking reads them back, which is how files and protocols are built.

What you will see

struct.error

About 1 min of reading

Try It

Pack and unpack a record.

1

Pack two short numbers.

import struct print(struct.pack("

Before you run it, say out loud what it will print. Then open the answer.

What it prints
b'\x01\x00\x02\x00'
2

Read them back with the same layout.

import struct print(struct.unpack("

Before you run it, say out loud what it will print. Then open the answer.

What it prints
(1, 2)
3

Show an int is four bytes.

import struct print(len(struct.pack("

Before you run it, say out loud what it will print. Then open the answer.

What it prints
4
4

Ask for four bytes from two and fail.

import struct print(struct.unpack("

Before you run it, say out loud what it will print. Then open the answer.

What it prints
struct.error
The layout expects four bytes, so two bytes is a size mismatch.

Data Flow

A layout states the size and order of each field, and packing writes values into it while unpacking reads them back.
1 layout states field sizes
↓
2 pack writes bytes
↓
3 unpack reads them back
↓
4 a wrong layout fails
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

import struct sets nothing on screen
print(struct.pack("<HH", 1, 2)) b'\x01\x00\x02\x00'
Why this works
The layout is the format

A binary file is a sequence of fields with known sizes. The format string records that layout, and both the writer and the reader use it.

Version the layout

If a format changes size, old files read wrong. Real formats put a version or a length at the front so a reader can tell which layout to use.

Examples

Pack a record

import struct
print(struct.pack("<HH", 1, 2))

Two unsigned shorts, little-endian, make four bytes. The format string is the layout of the record.

Read it back

import struct
print(struct.unpack("<HH", struct.pack("<HH", 1, 2)))

Unpacking with the same layout returns the values. The layout is the contract between writer and reader.

Fixed sizes

import struct
print(len(struct.pack("<I", 1)))

An unsigned int is always four bytes here. Fixed sizes are what let a reader know where each field starts.

A wrong layout

import struct
print(struct.unpack("<I", b"\x01\x02"))

Asking for four bytes from two fails. The layout has to match the bytes, which is the check that catches drift.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Byte order
Previous Lesson
Byte order
Next Lesson
Reading a file as bytes
Reading a file as bytes →