How Python Handles Binary Data
Explore Python's built-in tools for working with raw bytes, from files and structs to memory views and arrays. Learn how to read, write, and interpret binary data without dropping down to C.
How Python Handles Binary Data (And Why You Should Care)
You might think binary data is something only low-level programmers deal with--the kind of folks who write device drivers or network protocols in C. But Python actually makes working with raw bytes surprisingly intuitive, and once you understand how it works, you'll start seeing opportunities to use it everywhere.
Let me show you what I mean.
The Big Difference Between Text and Binary
Here's something that trips up a lot of Python developers: when you open a file with open("data.txt"), Python treats it as text. It assumes you're dealing with characters, so it tries to decode the bytes into strings.
But what if you're reading an image, a zip file, or a custom binary format? That's when you need to tell Python "no, give me the raw bytes."
# This gives you a string
with open("photo.jpg") as f:
data = f.read() # Python tries to decode this as text
# This gives you bytes
with open("photo.jpg", "rb") as f:
data = f.read() # Python gives you the raw bytes
The "rb" mode is the key difference. The "b" stands for binary, and it changes everything.
Working with Raw Bytes
When you read a file in binary mode, Python doesn't give you a string. It gives you a bytes object. A bytes object looks similar to a string but has some important differences:
data = b"Hello" # This is bytes, not a string
print(data[0]) # Output: 72 (the ASCII value of 'H')
Notice that indexing into bytes gives you an integer, not a character. This makes sense when you think about it: bytes are numbers between 0 and 255, and Python treats them as such.
The Bytes vs Bytearray Distinction
Python actually has two main types for handling binary data: bytes and bytearray. The difference is mutability.
# bytes is immutable
data = b"hello"
# data[0] = 72 # This would raise an error
# bytearray is mutable
data2 = bytearray(b"hello")
data2[0] = 72 # This works fine
If you're manipulating binary data on the fly, bytearray is usually more practical. If you're just reading something read-only, bytes is fine.
The struct Module: Your Binary Swiss Army Knife
Here's where things get really interesting. When you're dealing with binary file formats or network protocols, raw bytes aren't enough--you need to interpret them as meaningful data types like integers, floats, and strings.
Python's struct module shines here. It lets you pack and unpack binary data using format strings that describe exactly how the bytes should be interpreted.
import struct
# Pack data into bytes
packed = struct.pack('>i4s', 42, b'Test')
# '>' means big-endian, 'i' means integer, '4s' means 4-byte string
# Unpack bytes into data
number, text = struct.unpack('>i4s', packed)
print(number) # 42
print(text) # b'Test'
The format specifiers might look cryptic at first, but they're incredibly powerful. Need to parse a 32-bit floating point number from a binary file? 'f' handles that. Working with network protocols that use network byte order? '!' (which is shorthand for big-endian network order) has you covered.
Real-World Example: Reading a PNG File Header
Let me show you something practical. PNG images have a specific binary header structure. With Python, you can actually parse this without any image library:
import struct
with open("image.png", "rb") as f:
# Read the first 8 bytes (PNG signature)
signature = f.read(8)
# Read the IHDR chunk header
chunk_length = struct.unpack('>I', f.read(4))[0]
chunk_type = f.read(4)
if chunk_type == b'IHDR':
# Read width, height, bit depth, color type, etc.
width, height = struct.unpack('>II', f.read(8))
print(f"Image dimensions: {width}x{height}")
Now you're reading binary data like a pro. You don't need to use PIL or OpenCV just to get basic information from an image.
Memory Views and the array Module
When you're dealing with large amounts of binary data, performance matters. Python provides the memoryview object to work with slices of binary data without copying:
data = bytearray(b"Hello World")
view = memoryview(data)
slice = view[0:5]
# slice is a memoryview, not a copy of the data
# Modifying slice also modifies data
For working with homogeneous binary arrays (all the same data type), Python's array module is more memory efficient than lists:
from array import array
# Create an array of unsigned integers
data = array('I', [1, 2, 3, 4, 5])
# Write to a file
with open("numbers.bin", "wb") as f:
data.tofile(f)
# Read it back
recovered = array('I')
with open("numbers.bin", "rb") as f:
recovered.fromfile(f, 5)
When Binary Data Makes Sense
Not everything needs to be binary. Here are the scenarios where I reach for Python's binary tools:
- Reading or writing file formats (images, audio, archives)
- Network programming (protocol buffers, custom protocols)
- Interfacing with hardware (serial ports, device drivers)
- Working with legacy data (old database files, proprietary formats)
- Performance-critical sections (binary operations are faster than text parsing)
The Bottom Line
Python's binary data handling is one of those features that quietly power a lot of real-world applications. The bytes and bytearray types give you the raw building blocks, while struct lets you interpret that data as meaningful values. And tools like memoryview and the array module ensure you can do it efficiently.
The next time you're debugging a network protocol or trying to read a custom binary file format, remember: Python has your back. You don't need to drop down to C or shell out to external tools. The power is already there, waiting in the standard library.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.