Binary file programs: search, append, update

Part of the free LogicWiz Python course, Pack A: CBSE Class 12 Board Pack. The examples and practice run in the app.

The 5-mark question in the board sample paper says: add employee records to a binary file, then update the salary of everyone in IT. Many students lose 3 of those 5 marks because they change the record in memory and never write it back to the file.

You'll learn to

  • Search a binary file by roll number and say "not found" correctly
  • Append new records with ab
  • Update or delete records with read-modify-rewrite

Learn

The three board patterns

Almost every binary-file question is one of these:

  • Search: read record by record; compare; set a found flag.
  • Append: open with ab and dump() the new record.
  • Update / delete: read all records into a list, change (or drop) the ones you want, then open with wb and dump them all back.

Why rewrite everything? Pickled records have different sizes ("Ananya" is longer than "Riya"), so you cannot safely overwrite one record in the middle of the file.

Search by roll number

import pickle
with open("stud.dat", "wb") as f:
    for rec in ([101, "Aarav", 91], [102, "Meher", 78], [103, "Kabir", 85]):
        pickle.dump(rec, f)

def search(roll):
    found = False
    with open("stud.dat", "rb") as f:
        try:
            while True:
                rec = pickle.load(f)
                if rec[0] == roll:
                    print("Name:", rec[1], "| Marks:", rec[2])
                    found = True
        except EOFError:
            pass
    if not found:
        print("Roll", roll, "not found")

search(102)
search(110)

Print "not found" after the loop, using a flag. If you put it in an else: next to the if, it prints once for every record that doesn't match: three "Not found" lines before the right answer.

Append records typed by the user

import pickle

def append_student():
    roll = int(input("Roll: "))
    name = input("Name: ")
    marks = float(input("Marks: "))
    with open("stud.dat", "ab") as f:      # ab: add at the end
        pickle.dump([roll, name, marks], f)

append_student()
append_student()

with open("stud.dat", "rb") as f:
    try:
        while True:
            print(pickle.load(f))
    except EOFError:
        pass

ab also creates the file if it does not exist yet, so this works on a fresh folder.

Update: read all, change, rewrite

import pickle
with open("stud.dat", "wb") as f:
    for rec in ([101, "Aarav", 91], [102, "Meher", 78], [103, "Kabir", 85]):
        pickle.dump(rec, f)

def update_marks(roll, new_marks):
    records = []                                  # step 1: read all
    with open("stud.dat", "rb") as f:
        try:
            while True:
                records.append(pickle.load(f))
        except EOFError:
            pass
    changed = 0
    for rec in records:                           # step 2: change in the list
        if rec[0] == roll:
            rec[2] = new_marks
            changed += 1
    with open("stud.dat", "wb") as f:             # step 3: rewrite all
        for rec in records:
            pickle.dump(rec, f)
    return changed

print("Updated:", update_marks(102, 88))
print("Updated:", update_marks(999, 50))
with open("stud.dat", "rb") as f:
    pickle.load(f)
    print(pickle.load(f))

Deleting is the same three steps, except in step 2 you keep only the records whose roll does not match.

The textbook rb+ way (seek and tell)

import pickle
with open("stud.dat", "wb") as f:
    for rec in ([101, "Aarav", 91], [102, "Meher", 78], [103, "Kabir", 85]):
        pickle.dump(rec, f)

f = open("stud.dat", "rb+")
try:
    while True:
        pos = f.tell()              # where this record starts
        rec = pickle.load(f)
        if rec[0] == 102:
            rec[2] = 95
            f.seek(pos)             # go back to its start
            pickle.dump(rec, f)     # overwrite it in place
            break
except EOFError:
    pass
f.close()

with open("stud.dat", "rb") as f:
    print(pickle.load(f), pickle.load(f), pickle.load(f))

Many textbooks show this. It only works when the new record pickles to exactly the same size (78 → 95 does). Change a name to a longer one and the next record gets corrupted. The read-all-and-rewrite way is always safe.

Section E, 5 marks (2 + 3): (i) input a record and append it, (ii) update records that match a condition. Marks go to: import pickle, ab for append, the load() loop with EOFError, the condition, and writing the changed data back (wb + dump()). Use exactly the function names the question gives.

In one line

Search with a found flag; append with ab; update or delete by reading all records, changing the list, and rewriting with wb.