Free Study Material
Every text file you've worked with so far stores data as plain, human-readable characters — Python has to convert your numbers, dictionaries, and lists into text and back again every time. A binary file skips that translation entirely: it stores Python objects in their raw, native byte form. That makes binary files faster to process and able to hold data types a text file simply can't represent cleanly, like a whole dictionary or a list of objects.
Python doesn't do this conversion by itself — it needs a tool. That tool is the pickle module, part of Python's standard library. "Pickling" is the process of converting a Python object into a byte stream ready to save; "unpickling" is reversing it back into the original object.
import pickle
data = {"name": "Rahul", "score": 88}
byte_stream = pickle.dumps(data)
print("Original object:", data)
print("Pickled (as bytes):", byte_stream)
pickle.dumps() converts the dictionary into a byte stream — the printed output shows a string of raw bytes, not readable text, which is exactly why binary files aren't meant to be opened in Notepad. import pickle is the first step of every binary file program you'll write in this chapter.
Note: Text files are for humans to read directly. Binary files are for programs to read efficiently — you'll almost never open one in Notepad and expect it to make sense.
Binary files store data in Python's own native format instead of as text, making them faster and able to hold complex objects directly. The pickle module is what does the actual conversion, in both directions.
Text files are great for humans, but slow for computers. To store a List or Dictionary in a text file, Python has to convert it to a String first.
Binary Files store data exactly as it appears in the computer's memory (RAM). No translation is needed. This makes them faster and more secure.
The process of converting a Python object (like a List) into a byte stream is called Pickling (or Serialization). The reverse is called Unpickling.
To do this, we MUST import a special module at the top of our code:
import pickle
# -----------------------------------------------------
# CHALLENGE: Import the Tools
# -----------------------------------------------------
# Before we can work with binary files, we need the tool.
#
# YOUR TASK:
# 1. Type 'import pickle' on line 1.
# 2. Run the code. If it works, you will see the success message.
# TODO: Add the import statement below
print("Pickle module is ready to use!")
You already know "r", "w", and "a" from text files. Binary files use the exact same letters, with one addition: a "b" appended to the mode — "rb", "wb", "ab". That single letter tells Python "treat this data as raw bytes, not text," which matters because binary data (like a pickled dictionary) isn't valid text and can't be safely interpreted as characters.
| Mode | Meaning |
|---|---|
| wb | Write Binary — creates/overwrites a binary file |
| rb | Read Binary — reads an existing binary file |
| ab | Append Binary — adds to the end without erasing |
import pickle
with open("game_save.dat", "wb") as f:
print("File Created:", f.name)
print("File Mode:", f.mode)
Warning: Opening a binary file without the "b" flag (using plain "r" instead of "rb") doesn't necessarily crash — but Python will try to decode the raw bytes as text, which usually fails or produces corrupted data.
Binary modes are the same as text modes with a "b" added — wb, rb, ab — and that letter is what tells Python to handle the file's contents as raw bytes instead of attempting a text decode.
Binary files are strict. You cannot open them with standard text modes like 'r' or 'w'. You must add the letter 'b' to tell Python: "This is raw data."
Creates a new file. Warning: Deletes old content instantly!
Opens an existing file to read data. Crashes if file is missing.
Adds new records to the end without deleting old ones.
# -----------------------------------------------------
# CHALLENGE: Open a Binary File
# -----------------------------------------------------
# We want to create a file named "game_save.dat".
# Since it's a binary file, we need the "b" flag.
#
# YOUR TASK:
# 1. Fill in the open() function.
# 2. Use the mode "wb" (Write Binary).
# 3. Check the filename output.
import pickle
# TODO: Complete the line below
f = open("game_save.dat", )
print("File Created:", f.name)
print("File Mode:", f.mode)
f.close()
Saving a Python object to a binary file is a two-part job: open the file in "wb" mode, then hand the object to pickle.dump() along with the open file. pickle.dump(object, file) does the actual conversion — it takes any picklable Python object (a dictionary, list, number, custom object) and writes its byte representation straight into the file.
import pickle
game_data = {
"player": "Arjun",
"score": 500,
"level": 5
}
with open("savegame.dat", "wb") as f:
pickle.dump(game_data, f)
print("Game saved successfully!")
Notice game_data is a whole dictionary — not a string. pickle.dump() handles that directly; there's no need to convert it to text first, unlike writing to a text file.
Note: pickle.dump() always takes two arguments — the object to save, and the open file to save it into, in that order.
pickle.dump(object, file) writes a Python object directly into an already-open binary file — no manual conversion to text required, unlike .write() on a text file.
Writing to a binary file is called Dumping. Imagine moving houses. You don't carry every spoon individually. You dump them into a Moving Box.
The pickle.dump() function takes your Python object (List, Dictionary, etc.) and "dumps" it directly into the binary file. No conversion to string is needed!
You need two things: The Object you want to save, and the File you want to save it in.
'wb' mode).import pickle
# The data we want to save
game_data = {
"player": "Arjun",
"score": 500,
"level": 5
}
# TODO: Fill in the missing parts below
# 1. Open "savegame.dat" in "wb" mode
with open("savegame.dat", ) as f:
# 2. Dump the game_data into f
pickle.dump( , f)
print("Game saved successfully!")
Reading a pickled object back is the mirror image of writing one: open the file in "rb" mode, then call pickle.load(file). The key difference from text-file reading is what you get back — pickle.load() doesn't return a string you need to parse. It returns the exact same type of object that was originally saved: a dictionary comes back as a real dictionary, a list comes back as a real list, ready to use immediately.
import pickle
player = {"name": "Aisha", "score": 4200}
with open("savegame.dat", "wb") as f:
pickle.dump(player, f)
with open("savegame.dat", "rb") as f:
loaded_data = pickle.load(f)
print("Game Loaded Successfully!")
print("Player Data:", loaded_data)
loaded_data here is a genuine Python dictionary — you can immediately do loaded_data["score"] without splitting or converting anything.
Tip: This is the whole point of binary files over text files for structured data: no manual parsing. What you pickled is exactly what you get back.
pickle.load(file) reads a binary file and reconstructs the original Python object — dictionary in, dictionary out — with no text parsing needed on your part.
Reading from a binary file is called Unpickling (or Loading). It's like opening your moving box and pulling out your lamp. It comes out fully assembled, not as a pile of parts.
The pickle.load() function reads the bytes from the file and reconstructs the original Python object (List, Dictionary, etc.). You don't need to parse strings or convert numbers!
You need a variable to catch the object coming out of the file.
'rb' mode).import pickle
# TODO: Fill in the missing parts below
# 1. Open "savegame.dat" in "rb" mode
with open("savegame.dat", ) as f:
# 2. Load the data into a variable named 'loaded_data'
loaded_data = pickle.load(f)
print("Game Loaded Successfully!")
print("Player Data:", loaded_data)
# Notice: It prints a real Dictionary, not a string!
A binary file can hold many separate pickled records one after another — but unlike a text file, there's no simple way to ask "how many records are in this file?" before reading. The standard pattern is to read in a loop, while True, calling pickle.load() again and again — but that raises the obvious question: how does the loop know when to stop?
The answer is that it doesn't know in advance — it keeps reading until pickle.load() itself fails, because there's nothing left to read. That failure raises a specific, expected exception: EOFError (End Of File Error). Catching that exact exception and using it as the loop's exit signal is the standard, correct way to read every record in a binary file.
import pickle
with open("numbers.dat", "wb") as f:
pickle.dump(10, f)
pickle.dump(20, f)
pickle.dump(30, f)
with open("numbers.dat", "rb") as f:
try:
while True:
n = pickle.load(f)
print("Got number:", n)
except EOFError:
pass # reaching the end is expected, not an error to report
Warning: Without the try/except EOFError around it, this exact while True loop never stops on its own — it will crash with an unhandled EOFError instead of exiting cleanly, which is exactly the "infinite loop" problem this pattern exists to solve.
Reading an unknown number of records from a binary file uses while True combined with try / except EOFError: break (or pass, inside a with) — the EOFError isn't a bug, it's how Python tells you that you've reached the end of the file.
Reading a binary file is like taking cookies from a jar with your eyes closed. You keep grabbing cookies (objects) one by one.
When the jar is empty, you don't just grab "air". Your hand hits the bottom. In Python, this "hitting the bottom" is an Error called EOFError (End of File Error).
Unlike text files, which simply stop, `pickle.load()` CRASHES your program if it tries to read past the end. We must handle this crash.
To read all records safely, we use a specific coding pattern. You will memorize this for your exams.
import pickle
with open("data.dat", "rb") as f:
try:
while True: # 1. Run forever
data = pickle.load(f) # 2. Try to load
print(data)
except EOFError:
pass # 3. Stop gracefully when empty
import pickle
# Create a dummy file with 3 numbers
with open("numbers.dat", "wb") as f:
pickle.dump(10, f)
pickle.dump(20, f)
pickle.dump(30, f)
# Reading the file
print("Reading numbers...")
with open("numbers.dat", "rb") as f:
# TODO: Add 'try:' here
while True:
n = pickle.load(f)
print("Got number:", n)
# TODO: Add 'except EOFError: break' here
Adding a new record to a binary file without disturbing existing ones works exactly like it does for text files — open in "ab" (Append Binary) mode, and pickle.dump() adds the new record onto the end.
Searching is different, because binary files have no built-in "find" operation. The only way to search is the pattern from the previous topic: read every record in a loop until EOFError, and check each one yourself with an if condition as you go.
import pickle
def add_record():
name = input("Enter Name: ")
roll = int(input("Enter Roll No: "))
data = {"Name": name, "Roll": roll}
with open("students.dat", "ab") as f:
pickle.dump(data, f)
print("Record Added Successfully!")
def search_student(target_roll):
found = False
with open("students.dat", "rb") as f:
try:
while True:
rec = pickle.load(f)
if rec["Roll"] == target_roll:
print("Found Student:", rec)
found = True
break
except EOFError:
pass
if not found:
print("Student not found.")
Both functions need to actually run to do anything — call add_record() and enter a name and roll number when prompted, then call search_student() with that same roll number:
add_record()
search_student(101)
add_record() writes one record to students.dat. search_student(101) then loads every record in the file one at a time and checks each Roll — if you entered 101, it prints Found Student: {...}; any other roll number prints Student not found.
Note: Searching a binary file is always a linear scan — load each record, check it, move on. There's no shortcut; that's simply how sequential binary files work.
"ab" mode appends new records without touching existing ones. Searching means reading every record in a loop and checking each with an if, since binary files have no direct lookup — stopping early with break once a match is found.
If you use 'wb' mode, it acts like a bulldozer—it clears the file before adding new data. To simply add a new student to your list, you must use Append Binary mode ('ab').
The syntax is identical to writing, just the mode changes. It places the cursor at the very end of the file so your old data remains safe.
import pickle
def add_record():
# 1. Get data from user
name = input("Enter Name: ")
roll = int(input("Enter Roll No: "))
data = {"Name": name, "Roll": roll}
# 2. Open in 'ab' mode (Append Binary)
with open("students.dat", "ab") as f:
pickle.dump(data, f)
print("Record Added Successfully!")
# Run it twice to see it stack up!
add_record()
Binary files don't have a "Find" button (Ctrl+F). To find a specific record (e.g., Roll No 12), you have to perform a Linear Search.
'rb' mode.if record['Roll'] == 12:break (stop looking).import pickle
def search_student(target_roll):
found = False
with open("students.dat", "rb") as f:
try:
while True:
rec = pickle.load(f)
# TODO: Check if rec['Roll'] matches target_roll
if :
print("Found Student:", rec)
found = True
break # Stop checking after finding match
except EOFError:
pass # End of file reached
if found == False:
print("Student not found.")
# Let's search for the ID you just entered
search_student(int(input("Search for Roll No: ")))
Text or binary, a fundamental limitation is the same: you cannot edit one record "in place" in the middle of a file without risking corrupting everything after it. Records aren't guaranteed to be the same size, so there's no safe way to just overwrite one and leave the rest alone.
The standard, safe approach is always the same three steps:
"wb" mode) using the updated list.It looks wasteful, but for the file sizes you'll work with, it's simple, reliable, and avoids ever corrupting existing data.
import pickle
def update_marks(target_roll, new_marks):
all_students = []
found = False
with open("students.dat", "rb") as f:
try:
while True:
rec = pickle.load(f)
all_students.append(rec)
except EOFError:
pass
for rec in all_students:
if rec['Roll'] == target_roll:
rec['Marks'] = new_marks
found = True
if found:
with open("students.dat", "wb") as f:
for rec in all_students:
pickle.dump(rec, f)
print("File Saved Successfully.")
else:
print("Roll No not found.")
Warning: Step 3 opens the file in "wb" mode, which erases it first — this is only safe because step 1 already loaded every record into all_students. Skip step 1 and this pattern destroys your data instead of updating it.
Calling it end to end, on a fresh record that doesn't have a Marks field yet:
with open("students.dat", "wb") as f:
pickle.dump({"Name": "Meera", "Roll": 101}, f)
update_marks(101, 95)
with open("students.dat", "rb") as f:
try:
while True:
print(pickle.load(f))
except EOFError:
pass
This prints File Saved Successfully., then {'Name': 'Meera', 'Roll': 101, 'Marks': 95} — rec['Marks'] = new_marks adds the field even though the original record never had one, since pickle just saves whatever the dictionary looks like at that moment.
Updating a record in a binary file means read-all → modify-in-memory → rewrite-all, using "wb" only at the very end, after every existing record is already safely held in a list.
You cannot easily delete or change a line in the middle of a binary file. It's like a videotape—if you want to fix a scene in the middle, you often have to re-record the whole thing.
The standard strategy for Class 12 exams is:
'wb' mode (which wipes the old data).
import pickle
def update_marks(target_roll, new_marks):
all_students = []
found = False
# 1. READ EVERYTHING into memory
with open("students.dat", "rb") as f:
try:
while True:
rec = pickle.load(f)
all_students.append(rec)
except EOFError:
pass
# 2. MODIFY the record in the list
for rec in all_students:
# rec is a dictionary: {'Name': 'Amit', 'Roll': 101, 'Marks': 85}
if rec['Roll'] == target_roll:
rec['Marks'] = new_marks # Change the value
found = True
print("Record Updated in Memory:", rec)
# 3. REWRITE the file (The dangerous part!)
if found:
# TODO: Open file in 'wb' mode to overwrite old data
with open("students.dat", ) as f:
for rec in all_students:
pickle.dump(rec, f)
print("File Saved Successfully.")
else:
print("Roll No not found.")
# Usage: Update Roll 101's marks to 99
# update_marks(101, 99)
There is a faster way using rb+ mode and seek(), but it is risky. It allows you to jump to a specific record and overwrite just that one spot.
Note: For Board Exams, the "Rewrite All" method (above) is safer and preferred because records can vary in size.
You now have the complete binary-file toolkit:
- Write: "wb" mode + pickle.dump(object, f) to save a Python object directly. - Read: "rb" mode + pickle.load(f) to get the exact original object back. - Read many records: while True + try / except EOFError — the standard way to read until the file ends. - Append: "ab" mode adds a new record without touching existing ones. - Search: read every record in a loop and check each one with if — there's no shortcut. - Update: read everything into a list, modify the one record you need, then rewrite the whole file in "wb" mode.
The program below chains all six operations together on one file, class12.dat — creating student records, listing them, searching, and updating a mark — the same shape a real exam question or project builds on.
Tip: Almost every binary-file bug in this chapter comes from one of two mistakes: forgetting the "b" in the mode, or forgetting the try/except EOFError around a read loop. Check those two things first whenever something breaks.
Every binary file operation in this chapter follows the same underlying idea: pickle converts Python objects to and from raw bytes, wb/rb/ab control what you're allowed to do with the file, and EOFError is how Python tells a read loop it has reached the end.
Everything you need to know about Binary Files in one table.
| Function | Syntax | Use Case |
|---|---|---|
| import pickle | import pickle |
Mandatory 1st line. Enables serialization. |
| dump() | pickle.dump(data, f) |
Writes (saves) an object to the file. |
| load() | data = pickle.load(f) |
Reads (retrieves) the next object. |
| EOFError | except EOFError: |
Catches the "End of File" crash during loops. |
| seek() | f.seek(offset) |
Moves the file cursor (0 = Start). |
import pickle
filename = "class12.dat"
# -------------------------------------------------
# 1. WRITE (Create/Add Data)
# -------------------------------------------------
def create_file():
students = [
{"Roll": 1, "Name": "Amit", "Marks": 85},
{"Roll": 2, "Name": "Priya", "Marks": 92},
{"Roll": 3, "Name": "Rahul", "Marks": 78}
]
with open(filename, "wb") as f:
for s in students:
pickle.dump(s, f)
print("Step 1: File created with 3 students.")
# -------------------------------------------------
# 2. READ (Display All)
# -------------------------------------------------
def read_file():
print("\n--- Current Data ---")
with open(filename, "rb") as f:
try:
while True:
rec = pickle.load(f)
print(rec)
except EOFError:
pass
# -------------------------------------------------
# 3. SEARCH (Find Specific)
# -------------------------------------------------
def search_student(roll):
print(f"\n--- Searching for Roll {roll} ---")
found = False
with open(filename, "rb") as f:
try:
while True:
rec = pickle.load(f)
if rec["Roll"] == roll:
print("Found:", rec)
found = True
except EOFError:
pass
if not found: print("Not Found.")
# -------------------------------------------------
# 4. UPDATE (Modify Data)
# -------------------------------------------------
def update_marks(roll, new_marks):
print(f"\n--- Updating Roll {roll} to {new_marks} ---")
all_data = []
# Read All
with open(filename, "rb") as f:
try:
while True:
all_data.append(pickle.load(f))
except EOFError: pass
# Modify in Memory
with open(filename, "wb") as f:
for rec in all_data:
if rec["Roll"] == roll:
rec["Marks"] = new_marks
pickle.dump(rec, f)
print("Update Complete.")
# --- MAIN EXECUTION ---
create_file() # 1. Create
read_file() # 2. Verify
search_student(2) # 3. Find Priya
update_marks(1, 99) # 4. Change Amit's marks
read_file() # 5. Verify Update