How to Read a Binary File into a Byte Array in Python

I once built a small document-processing script for a 10-person insurance office in Denver. The office needed to inspect employee badge images and signed PDF forms before archiving them. Reading those files as text caused decoding errors because images and PDFs contain raw binary data, not readable characters.

The reliable fix was to read a binary file into a byte array in Python. A byte array stores each byte as an integer from 0 through 255, and you can change those values when needed.

This step-by-step guide shows the simplest approach, safer reusable functions, large-file handling, and practical byte inspection.

What Happens When You Read a Binary File Into a Byte Array in Python?

A binary file stores data as bytes rather than plain text. Common examples include JPEG images, PDF documents, ZIP archives, audio files, and compiled programs. A text editor may show random symbols when you open one because it tries to decode those bytes as characters.

Python provides two closely related types for binary data:

  • bytes stores an immutable sequence, which means you cannot change individual values.
  • bytearray stores a mutable sequence, so you can update, append, or remove bytes.

When you call file.read() in binary mode, Python returns a bytes object. Passing that result to bytearray() creates the mutable byte array. This distinction matters when your script needs to replace a header flag, mask private values, or build a network packet.

raw_bytes = b"\x50\x59\x01\x02"
data = bytearray(raw_bytes)

print(type(raw_bytes))
print(type(data))
print(list(data))

Output:

<class 'bytes'>
<class 'bytearray'>
[80, 89, 1, 2]

You can refer to the screenshot below to see the output.

Read a Binary File into a Byte Array in Python

The first two integers, 80 and 89, represent the ASCII letters P and Y. The last two values represent raw control bytes. If you need more background on Python’s immutable binary type, see how to convert a string to bytes in Python.

Set Up a Small Binary File for the Python Tutorial

I recommend using Python 3.10 or newer on a local Windows, macOS, or Linux machine. This guide uses only built-in modules, so you do not need to install a package.

Create a folder named byte_array_demo with this simple structure:

byte_array_demo/
├── create_sample.py
├── read_binary.py
└── files/
    └── employee_badge.bin

The module create_sample.py is simply a Python file containing reusable code. Add this code to create a predictable test file for a fictional employee named Olivia Carter:

from pathlib import Path

file_path = Path("files/employee_badge.bin")
file_path.parent.mkdir(exist_ok=True)

sample_data = b"OC" + bytes([1, 42, 128, 255])
file_path.write_bytes(sample_data)

print(f"Created {file_path}")
print(f"Saved {len(sample_data)} bytes")

Output:

Created files/employee_badge.bin
Saved 6 bytes

You can refer to the screenshot below to see the output.

How to Read Binary File into a Byte Array in Python

The b prefix creates a bytes literal. The sample starts with the initials OC, followed by a version byte, an employee code, and two values that do not fit normal ASCII text. Using a small controlled file makes each later output easy to verify.

The pathlib module represents file paths as objects and works across operating systems. This avoids manually joining folders with Windows backslashes or macOS and Linux forward slashes. You can also review these practical ways to get the directory of a file in Python when your script and data live in different folders.

Read a Binary File Into a Byte Array in Python With open()

The clearest beginner approach uses Python’s built-in open() function. Set the mode to "rb", where r means read and b means binary. Binary mode tells Python not to decode the file as text.

Add this code to read_binary.py:

file_path = "files/employee_badge.bin"

with open(file_path, "rb") as binary_file:
    file_bytes = binary_file.read()

badge_data = bytearray(file_bytes)

print(badge_data)
print(type(badge_data))
print(f"Byte count: {len(badge_data)}")
print(f"Integer values: {list(badge_data)}")

Output:

bytearray(b'OC\x01*\x80\xff')
<class 'bytearray'>
Byte count: 6
Integer values: [79, 67, 1, 42, 128, 255]

The with statement creates a context manager, which closes the file automatically after the indented block finishes. I always use this pattern because it also closes the handle when an exception interrupts the read. You can explore the broader rules for opening a file in Python if file modes are new to you.

The call to read() loads the full file into memory as bytes. The bytearray() constructor then copies that data into a mutable container. This two-step conversion makes the returned type obvious to another developer reading your code.

Pro Tip: In my experience, forgetting the b in "rb" causes most beginner problems here. Text mode may raise a UnicodeDecodeError or silently alter newline bytes on some systems.

Read the file directly with pathlib

If your project already uses pathlib, its read_bytes() method provides a shorter alternative. The method still returns bytes, so wrap the result with bytearray().

from pathlib import Path

file_path = Path("files/employee_badge.bin")
badge_data = bytearray(file_path.read_bytes())

print(list(badge_data))
print(f"Loaded from: {file_path.name}")

Output:

[79, 67, 1, 42, 128, 255]
Loaded from: employee_badge.bin

You can refer to the screenshot below to see the output.

Read Binary File into a Byte Array in Python

I use this version for small automation scripts because it reads cleanly and handles paths consistently. I use open() when I need streaming, custom buffering, or several operations on the same file handle. Both approaches produce the same byte array.

Inspect the Byte Array Without Corrupting the File

Printing raw binary data rarely tells the whole story. I usually inspect the length, selected indexes, and hexadecimal representation before changing anything. Hexadecimal is a base-16 format that displays each byte with two characters, making headers and flags easier to recognize.

from pathlib import Path

badge_data = bytearray(
    Path("files/employee_badge.bin").read_bytes()
)

print(f"First byte: {badge_data[0]}")
print(f"First two bytes: {badge_data[:2]}")
print(f"Hex: {badge_data.hex(' ')}")
print(f"Last byte: {badge_data[-1]}")

Output:

First byte: 79
First two bytes: bytearray(b'OC')
Hex: 4f 43 01 2a 80 ff
Last byte: 255

An index returns one integer, while a slice returns another byte array. Python starts indexes at zero, so badge_data[0] accesses the first byte. Negative index -1 accesses the final byte.

The .hex() method gives you a safe, compact diagnostic view. It does not decode binary content or change the original data. For the reverse operation, this guide to converting a hexadecimal string to bytes in Python explains how hex text becomes binary values.

Avoid calling .decode() unless you know that a section contains text and know its character encoding. An encoding maps bytes to readable characters, such as UTF-8. Arbitrary image or PDF bytes do not follow one text encoding.

Modify and Save a Python Byte Array

A byte array becomes useful when you need controlled edits. Suppose byte index 2 stores the badge format version, and index 3 stores Olivia’s department code. We can update both values without rebuilding the entire sequence.

from pathlib import Path

source_path = Path("files/employee_badge.bin")
output_path = Path("files/employee_badge_updated.bin")

badge_data = bytearray(source_path.read_bytes())
badge_data[2] = 2
badge_data[3] = 51

output_path.write_bytes(badge_data)

print(f"Updated bytes: {list(badge_data)}")
print(f"Saved to: {output_path}")

Output:

Updated bytes: [79, 67, 2, 51, 128, 255]
Saved to: files/employee_badge_updated.bin

You can refer to the screenshot below to see the output.

How to Read a Binary File into a Byte Array in Python

Every assigned value must remain between 0 and 255. Python raises a ValueError if you try badge_data[2] = 300. This limit exists because one byte holds 256 possible values.

Write to a new file while developing instead of overwriting the source. That simple habit gives you a known-good file for comparison. When you need more output patterns, review how to write bytes to a file in Python.

You can confirm that only the intended positions changed:

original = Path("files/employee_badge.bin").read_bytes()
updated = Path("files/employee_badge_updated.bin").read_bytes()

changes = [
    (index, old, new)
    for index, (old, new) in enumerate(zip(original, updated))
    if old != new
]

print(changes)

Output:

[(2, 1, 2), (3, 42, 51)]

This compact check reports each changed index, old value, and new value. I use a similar comparison before saving edited configuration records or device packets.

Read a Large Binary File Into a Byte Array in Python

Calling read() without a size loads the complete file into random-access memory, or RAM. That works well for a 2 MB image but creates unnecessary pressure with a 4 GB video. Your script needs memory for both the original bytes result and the new bytearray during conversion.

Check the size before choosing a strategy:

from pathlib import Path

file_path = Path("files/employee_badge.bin")
size = file_path.stat().st_size

print(f"File size: {size} bytes")

Output:

File size: 6 bytes

For additional reporting options, see how to get a file’s size in Python. For large inputs, process fixed-size chunks, which are smaller blocks read one at a time.

from pathlib import Path

file_path = Path("files/employee_badge.bin")
chunk_size = 4
combined = bytearray()

with file_path.open("rb") as binary_file:
    while chunk := binary_file.read(chunk_size):
        combined.extend(chunk)
        print(f"Read chunk: {list(chunk)}")

print(f"Combined: {list(combined)}")

Output:

Read chunk: [79, 67, 1, 42]
Read chunk: [128, 255]
Combined: [79, 67, 1, 42, 128, 255]

The walrus operator := assigns the chunk and checks whether it contains data. Python stops the loop when read() returns empty bytes at the end.

This example still combines every chunk, so the final byte array eventually occupies the full file size. If your goal is hashing, uploading, scanning, or copying, process each chunk inside the loop and do not keep combined. Chunking then keeps memory use nearly constant, regardless of file size.

Build a Safe Reusable Binary Reader Function

A function packages logic under a name so you can call it from several scripts. Production code should validate the path and enforce a practical size limit before allocating memory.

from pathlib import Path


def read_binary_as_bytearray(
    file_path: str | Path,
    max_bytes: int = 10_000_000,
) -> bytearray:
    """Read a small binary file and return mutable bytes."""
    path = Path(file_path)

    if not path.exists():
        raise FileNotFoundError(f"File not found: {path}")

    if not path.is_file():
        raise ValueError(f"Path is not a file: {path}")

    file_size = path.stat().st_size
    if file_size > max_bytes:
        raise ValueError(
            f"File is {file_size} bytes; limit is {max_bytes}"
        )

    return bytearray(path.read_bytes())


try:
    data = read_binary_as_bytearray(
        "files/employee_badge.bin",
        max_bytes=100,
    )
    print(f"Loaded {len(data)} bytes")
except (FileNotFoundError, PermissionError, ValueError) as error:
    print(f"Could not load file: {error}")

Output:

Loaded 6 bytes

The str | Path annotation documents the accepted argument types and requires Python 3.10 or newer. The default limit allows files up to 10 MB. Adjust it for your application and available memory rather than accepting every file blindly.

The try and except block provides error handling, which turns expected file problems into a clear message. FileNotFoundError covers a wrong path, PermissionError covers access restrictions, and ValueError covers our validation rules. Before reading user-selected files, you may also want to check if a file exists in Python.

Here is the failure output for a missing file:

try:
    read_binary_as_bytearray("files/madison_badge.bin")
except FileNotFoundError as error:
    print(error)

Output:

File not found: files/madison_badge.bin

Keep file access in a helper like this when a command-line application grows. Your input prompts, business rules, and byte parsing can then stay in separate modules.

Choose Between bytes, bytearray, and memoryview

Use bytes when you only need to read, compare, send, or hash binary data. It communicates that your code will not change the values. Many Python libraries also return bytes by default.

Use bytearray when you need in-place edits, appending, or deletion. Its methods resemble list operations, but every item must remain a byte-sized integer.

Use memoryview when performance matters and you need a window into existing binary data without copying it. A memory view shares the original buffer, so changes can affect the underlying byte array.

data = bytearray([10, 20, 30, 40])
middle = memoryview(data)[1:3]
middle[0] = 99

print(list(data))
print(list(middle))

Output:

[10, 99, 30, 40]
[99, 30]

For most beginner Python projects, start with bytes for read-only work and bytearray for edits. Reach for memoryview only after measurements show that copying large buffers creates a real performance issue. If you receive text after an API or socket operation, learn the correct boundary between binary and text with converting bytes to strings in Python.

Things to Keep in Mind

  • Always use binary mode: Open binary files with "rb". Text mode may decode, translate, or reject raw bytes.
  • Validate untrusted paths: Confirm that a path exists and points to a regular file. Do not let user input escape an approved data folder.
  • Set a size limit: A whole-file read can exhaust RAM or freeze a desktop tool. Inspect the size or process large files in chunks.
  • Preserve the original: Save modifications under a new filename until you have tested byte offsets and output behavior.
  • Know the file format: Changing arbitrary bytes can corrupt an image, archive, or document. Follow the format’s header and checksum rules.
  • Do not print secrets: Binary files may contain tokens, personal data, or embedded metadata. Log only the byte ranges needed for diagnosis.

Frequently Asked Questions

How do I read a binary file as a byte array in Python?

Open the file with "rb", call read(), and pass the result to bytearray(). Use a with block so Python closes the file even when an error occurs.

What is the difference between bytes and bytearray in Python?

bytes is immutable, so its values cannot change after creation. bytearray is mutable and supports index assignment, append(), extend(), and deletion.

Why do I get a UnicodeDecodeError when reading a binary file?

You likely opened the file in text mode or called .decode() on arbitrary bytes. Use binary mode and only decode sections that contain text in a known encoding.

Can Python read an image or PDF into a byte array?

Yes. Python reads JPEG, PNG, PDF, ZIP, and other formats with the same "rb" approach because they all contain bytes. Reading the data does not interpret the file’s internal format.

How can I read a large binary file without running out of memory?

Call read(chunk_size) inside a loop and process each chunk immediately. Do not append every chunk to one byte array unless you truly need the complete file in memory.

How do I convert a byte array back into a file?

Open a destination with "wb" and call write(byte_array), or pass the byte array to Path.write_bytes(). Prefer a new destination while testing so you retain the original.

You learned how to load, inspect, modify, validate, and save binary data with open, pathlib, and bytearray. For small files, read once in binary mode; for large files, process fixed-size chunks. I hope you found this article helpful.

You May Also Like

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.