LambdaLabTM
Computer Science · Class 12 · Binary Files
Binary filesReading⏱️ 14 min read

Reading a Binary File

load() reads one object. A file with three records therefore needs three calls — and the interesting question is what the fourth one does, because that is what tells your loop when to stop.

1One load(), one record

student.dat holds the three records written in the last lesson. Each load() takes the next one, in the order they were written:

four_loads.py
import pickle

f = open('student.dat', 'rb')

print(pickle.load(f))
print(pickle.load(f))
print(pickle.load(f))
print(pickle.load(f))

f.close()
Output
[1, 'Ravi', 78]
[2, 'Meera', 91]
[3, 'Amit', 65]
Traceback (most recent call last):
  File "four_loads.py", line 8, in <module>
    print(pickle.load(f))
          ^^^^^^^^^^^^^^
EOFError: Ran out of input
📖 Reading, one load() at a time

student.dat holds four records. Press load() and watch which one comes back.

student.dat
[1, 'Ravi', 78]
[2, 'Meera', 91]
[3, 'Amit', 65]
[4, 'Neha', 88]
end of file
nothing read yet

The file is open and the pointer is at the start. Nothing has been read.

EOFError is not a bug — it is the end of the file
EOF stands for End Of File. When a text file runs out, read() hands back an empty string and the program carries on. A binary file has no such value to hand back, so load() raises instead. The exception is the message that the records have finished.

2The loop every board answer uses

Since the end arrives as an exception, the loop is wrapped in try. Read for ever, and stop when the exception says to:

display_all.py
import pickle

f = open('student.dat', 'rb')
total = 0

try:
    while True:
        record = pickle.load(f)
        print(record[0], record[1], record[2])
        total = total + 1
except EOFError:
    pass

f.close()
print('Records read:', total)
Output
1 Ravi 78
2 Meera 91
3 Amit 65
Records read: 3
while True:

A loop with no condition. It has no way of knowing how many records there are — nothing in the file says so.

record = pickle.load(f)

One record. On the call after the last one, this line raises instead of returning.

except EOFError:

Which lands here. The loop is left, and the program carries on.

pass

There is nothing to do about it — reaching the end of a file is not a problem. pass is the body that says 'deliberately nothing'.

f.close()

Outside the try, so it runs whichever way the loop ended.

The close() goes outside the try
Put f.close() inside the try block and it is skipped the moment the exception is raised — which is every time, because that is how the loop ends. Outside, or in a finally, or use a with block.

3The other way it is written — try inside, then break

You will meet a second shape in textbooks and in board answers, and it is equally correct. The try moves inside the loop, and the except ends the loop with break:

display_all_break.py
import pickle

f = open('student.dat', 'rb')
total = 0

while True:
    try:
        record = pickle.load(f)
        print(record[0], record[1], record[2])
        total = total + 1
    except EOFError:
        break

f.close()
print('Records read:', total)
Output
1 Ravi 78
2 Meera 91
3 Amit 65
Records read: 3

Same output, same three records, same file. What changed is where the loop is when the exception happens, and that is what decides how the loop ends.

try outside — the loop is inside it
try:
    while True:
        record = pickle.load(f)
        ...
except EOFError:
    pass

The exception jumps out of the whole try block, and the loop is inside it, so the loop is abandoned on the way out. Nothing more is needed — hence pass.

try inside — the loop is around it
while True:
    try:
        record = pickle.load(f)
        ...
    except EOFError:
        break

The exception is caught inside the loop, so the loop is still running and would go round again. break is what stops it.

Key Takeaway
The two bodies are not interchangeable. pass belongs with try outside, break belongs with try inside. Match the body to where the try is.

4Forgetting the break hangs the program

This is worth doing on purpose once. Write the second shape but leave pass in the except, and the program never ends:

forgot_break.py
import pickle

f = open('student.dat', 'rb')

while True:
    try:
        record = pickle.load(f)
        print(record)
    except EOFError:
        pass          # <- should have been break

f.close()
Output
[1, 'Ravi', 78]
[2, 'Meera', 91]
[3, 'Amit', 65]
(and then nothing, for ever - the program has to be stopped by hand)

The three records print, and then it spins. The pointer is at the end of the file, so load() raises EOFError again — which is caught, and the loop goes round, and it raises again. Nothing moves the pointer and nothing ends the loop.

This is what a hung program looks like
No error, no crash, no output. Just a cursor that never comes back. Stop it with Ctrl + C in the terminal, or the stop button in IDLE. If one of your programs ever does this, an except with no way out is the first place to look.

5Reading the records into a list

Often you want all the records in memory — to count them, sort them, or change one and write them all back. Same loop, with append() instead of print():

into_a_list.py
import pickle

f = open('student.dat', 'rb')
records = []

try:
    while True:
        records.append(pickle.load(f))
except EOFError:
    pass

f.close()

print(records)
print('There are', len(records), 'records.')
Output
[[1, 'Ravi', 78], [2, 'Meera', 91], [3, 'Amit', 65]]
There are 3 records.
Tip
This is the shape the update and delete lessons both start with. Once the records are in an ordinary list, everything you learnt about lists in Class 11 applies to them.

6If the file is not there

'rb', like 'r', never creates a file. A program that offers to display records before any have been saved should say so politely:

guarded_read.py
import pickle

try:
    f = open('student.dat', 'rb')
    try:
        while True:
            print(pickle.load(f))
    except EOFError:
        pass
    f.close()
except FileNotFoundError:
    print('No records have been saved yet.')
Output
[1, 'Ravi', 78]
[2, 'Meera', 91]
[3, 'Amit', 65]
Two different exceptions, two different jobs
FileNotFoundError means there is no file at all. EOFError means the file exists and you have read all of it. Catching them in the same except would hide a real problem behind a normal one.

7Try it

The program below writes the file and then reads it back. Add a fourth record, or print the records in a nicer format:

write_then_read.py

8Recap

load() reads one object

Three records means three calls, in the order they were written.

EOFError marks the end

The call after the last record raises it. It is the normal way a binary read finishes, not a mistake.

try + while True + except EOFError

The standard loop. Write it the same way every time and it stops being something to think about.

pass is the right body — when try is outside

The exception leaves the whole try block, taking the loop with it, so there is nothing left to do.

break is the right body — when try is inside

The except sits inside the loop, so the loop would go round again. break is what ends it. Both shapes are accepted.

Swapping the two hangs the program

try inside with pass loops for ever: load() raises at the end of the file, the except swallows it, nothing moves the pointer. Ctrl + C.

close() outside the try

Inside, it would be skipped by the exception that ends every run.

FileNotFoundError is different

No file at all, rather than no records left. Catch it separately.

✍️ Now write these yourself
  1. 1

    Display every record with its position number in front of it.

    Hint · A counter that starts at 1 and grows inside the loop.

  2. 2

    Write the display program both ways — try outside with pass, and try inside with break — and check the output matches.

    Hint · It does. Either is accepted in an exam; write the one you remember.

  3. 3

    Put pass where the break belongs, run it, and stop it with Ctrl + C.

    Hint · Three records, then silence. Worth causing once so you recognise it later.

  4. 4

    Count the records without printing any of them.

    Hint · The same loop, with only the counter in the body.

  5. 5

    Read the records into a list, then print the last one.

    Hint · records[-1], once the loop has finished.

  6. 6

    Put the f.close() inside the try block and print f.closed afterwards.

    Hint · False — the exception jumped over it.

Quick Check

What happens when pickle.load() is called and no records are left?

Quick Check

You write the loop as while True: with try INSIDE it. What belongs in the except EOFError block?

Quick Check

A program prints all three records and then hangs, printing nothing and never finishing. What is the likely cause?

Quick Check

Why is the reading loop written as while True?

Quick Check

Where should f.close() go in the standard reading loop?

Quick Check

A program tries to read student.dat before any record has been saved. Which exception comes first?