Class 12Computer Science · Programming with PythonFull chapter

Binary Files

The whole chapter in one place — read it, then test yourself. Clear notes, a reference sheet, a practice quiz, and worked NCERT solutions & PYQs.

Binary Files and the Six Open Modes

Quick answer A binary file moves raw bytes instead of characters, so it needs a "b" mode — and rb, rb+, wb, wb+, ab, ab+ differ in exactly three things: whether the file must already exist, whether the old data survives, and where the position starts.

Every file on a disk is just a run of bytes. What changes is how Python interprets those bytes when you open it.

  • Text mode ("r", "w", "a") — Python decodes the bytes into a str for you and, on Windows, also translates line endings. You work with characters.
  • Binary mode ("rb", "wb", "ab") — Python hands you the bytes exactly as they sit on the disk, as a bytes object. No decoding, no line-ending translation.

Open the same file both ways and the difference shows up at once.

import os

f = open("marks.txt", "w")
f.write("Aarav,95\nDiya,88\n")
f.close()

f = open("marks.txt", "r")
print("TEXT mode :", repr(f.read()))
f.close()

f = open("marks.txt", "rb")
data = f.read()
print("BINARY mode:", data)
print("type       :", type(data))
print("size on disk:", os.path.getsize("marks.txt"), "bytes")
f.close()

Real output, run on Windows:

TEXT mode : 'Aarav,95\nDiya,88\n'
BINARY mode: b'Aarav,95\r\nDiya,88\r\n'
type       : 
size on disk: 19 bytes

Compare the two carefully. In memory the text is 17 characters, but the file on disk is 19 bytes — text mode quietly turned each \n into \r\n. For a marksheet that is harmless. For a photo, an .exe, or a pickled record it is fatal: any byte that merely happened to be 0x0A would be silently rewritten and the data destroyed. That is the entire reason binary mode exists.

Binary mode then imposes two rules you will hit within five minutes of starting.

f = open("test.dat", "wb")
try:
    f.write("Aarav")
except TypeError as e:
    print("TypeError:", e)
f.write(b"Aarav")
f.close()

try:
    f = open("nofile.dat", "rb")
except FileNotFoundError as e:
    print("FileNotFoundError:", e)

f = open("test.dat", "rb")
print("read back:", f.read())
print("closed?  ", f.closed)
f.close()
print("closed?  ", f.closed)
TypeError: a bytes-like object is required, not 'str'
FileNotFoundError: [Errno 2] No such file or directory: 'nofile.dat'
read back: b'Aarav'
closed?   False
closed?   True

So: you write bytes, and you read bytes. Building those bytes by hand for a student record would be miserable, and that is precisely the gap the pickle module fills in the next section.

The six binary modes. Three questions separate all of them — must the file already exist, does the old data survive, and where does the position start?

ModeIf file is missingExisting dataStart positionReadWrite
rbFileNotFoundErrorKeptByte 0YesNo
rb+FileNotFoundErrorKeptByte 0YesYes
wbCreatedErasedByte 0NoYes
wb+CreatedErasedByte 0YesYes
abCreatedKeptEnd of fileNoYes, always at the end
ab+CreatedKeptEnd of fileYesYes, always at the end

The + only ever adds a permission; it never protects your data. wb+ still wipes the file. The mode that catches students out is ab+, because its position starts at the end, not at 0.

import pickle

f = open("m.dat", "wb")
pickle.dump("AAA", f)
pickle.dump("BBB", f)
f.close()

f = open("m.dat", "ab+")
print("tell:", f.tell(), "| read() from here:", f.read())
f.seek(0)
print("after seek(0), load:", pickle.load(f))
pickle.dump("CCC", f)
f.close()

f = open("m.dat", "rb")
try:
    while True:
        print("  -", pickle.load(f))
except EOFError:
    f.close()
tell: 36 | read() from here: b''
after seek(0), load: AAA
  - AAA
  - BBB
  - CCC

Read both halves of that. read() returned b'' because the position was already at byte 36, the end of a 36-byte file. And even after seek(0), the new "CCC" still landed at the end. In append mode seek() repositions reading only — writes always go to the end. That is a guarantee of the operating system, not something Python can override.

One more split inside the append family: ab+ can read once you rewind, but plain ab cannot read at all.

f = open("ap.dat", "wb")
f.write(b"hello world")
f.close()

f = open("ap.dat", "ab")
print("ab  -> tell:", f.tell())
try:
    f.read()
except Exception as e:
    print("ab  -> read():", type(e).__name__, ":", e)
f.close()

f = open("ap.dat", "ab+")
print("ab+ -> tell:", f.tell(), " read():", f.read())
f.seek(0)
print("ab+ -> after seek(0), read():", f.read())
f.close()
ab  -> tell: 11
ab  -> read(): UnsupportedOperation : read
ab+ -> tell: 11  read(): b''
ab+ -> after seek(0), read(): b'hello world'

Both report tell() as 11, the size of the file, so neither starts at byte 0. But read() on the ab file raises io.UnsupportedOperation: read, while on ab+ it quietly returns b'' and only yields data after seek(0). That is what the No in the Read column means for ab — not "reads nothing", but "cannot read".

Finally, always close the file. close() flushes Python's buffer out to the disk; a program that ends without closing can leave records unwritten. The with statement closes for you, even if an exception is raised inside the block.

with open("m.dat", "wb") as f:
    pickle.dump({"city": "Pune", "pin": 411001}, f)
print("closed after the with-block?", f.closed)

with open("m.dat", "rb") as f:
    print(pickle.load(f))
closed after the with-block? True
{'city': 'Pune', 'pin': 411001}
Open a binary file f = open("student.dat", "rb") Returns a file object. With rb or rb+ a missing file raises FileNotFoundError; wb, wb+, ab and ab+ create it.
Close a file f.close() Flushes the buffer to disk and sets f.closed to True. Without it, records can be left unwritten.
Auto-close with a block with open("student.dat", "rb") as f: Closes the file on the way out of the block, even if an exception is raised inside it.
Current byte position pos = f.tell() Returns an int: the offset in bytes from the start. It is 0 just after opening in rb/rb+/wb/wb+, and equal to the file size in ab/ab+.
Move the position f.seek(offset) Jumps to that byte offset and returns the new position as an int. In ab+ it repositions reading only; writes still go to the end of the file.
Read raw bytes data = f.read() Returns a bytes object such as b'Aarav', never a str. f.read(n) reads at most n bytes.
Remember
  • Binary mode is chosen by putting a "b" in the mode string. It moves raw bytes, so no character decoding happens and no newline gets rewritten as a carriage-return pair.
  • In binary mode f.write() takes bytes and f.read() returns bytes. Passing a str raises TypeError: a bytes-like object is required, not 'str'.
  • rb and rb+ need the file to exist already and raise FileNotFoundError otherwise. wb, wb+, ab and ab+ all create the file if it is missing.
  • wb and wb+ erase everything in the file the instant you open it. This is the single most common way students destroy their own data file.
  • In ab and ab+ the position starts at the end of the file, and every write lands at the end no matter where you seek. Only ab+ can read: its read() returns b'' until you seek(0), while read() on a plain ab file raises io.UnsupportedOperation.

The pickle Module: dump() and load()

Quick answer pickle.dump() turns one Python object into bytes in a binary file and pickle.load() rebuilds exactly one object back, so a file of N records needs N loads, and the only signal that the file is finished is the EOFError that load() raises.

You cannot sensibly build a binary file of student records by hand — you would have to invent your own byte layout and then decode it. Python's pickle module does the whole job.

  • Pickling (serialisation): a live Python object becomes a stream of bytes.
  • Unpickling (deserialisation): that stream of bytes becomes a live Python object again.

pickle ships with Python, so import pickle is all the setup there is. It handles ints, floats, strings, lists, tuples, dictionaries and any nesting of them, which covers every record structure this syllabus asks for.

import pickle, os

stu = {"roll": 1101, "name": "Aarav Sharma", "marks": 95.5}

f = open("one.dat", "wb")
pickle.dump(stu, f)
f.close()

f = open("one.dat", "rb")
raw = f.read()
f.close()
print("raw bytes :", raw)
print("file size :", os.path.getsize("one.dat"), "bytes")

f = open("one.dat", "rb")
back = pickle.load(f)
f.close()
print("loaded    :", back)
print("type      :", type(back))
print("same value:", back == stu)
print("same object:", back is stu)
print("marks+5   :", back["marks"] + 5)
raw bytes : b'\x80\x04\x956\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04roll\x94MM\x04\x8c\x04name\x94\x8c\x0cAarav Sharma\x94\x8c\x05marks\x94G@W\xe0\x00\x00\x00\x00\x00u.'
file size : 65 bytes
loaded    : {'roll': 1101, 'name': 'Aarav Sharma', 'marks': 95.5}
type      : 
same value: True
same object: False
marks+5   : 100.5

Three things are worth reading off that output. First, load() gave back a genuine dictback["marks"] + 5 did real arithmetic and produced 100.5. Second, back is stu is False: unpickling builds a fresh copy, not a link to the original object. Third, look at the raw bytes — Aarav Sharma is sitting there in plain view.

Pickle is not encryption. It is a storage format, nothing more. Anyone with Python can call pickle.load() on your file, and the strings are visible even without that.

f = open("w.dat", "wb")
pickle.dump({"user": "priya", "upi_pin_hint": "1234"}, f)
f.close()
f = open("w.dat", "rb")
print("raw:", f.read())
f.close()
raw: b'\x80\x04\x95*\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04user\x94\x8c\x05priya\x94\x8c\x0cupi_pin_hint\x94\x8c\x041234\x94u.'

The username and the PIN hint are both readable. Never store a password, a UPI PIN or an Aadhaar number in a .dat file and call it secure. A file being binary makes it inconvenient to read, not protected. The risk runs the other way too: only unpickle files you produced yourself, because load() rebuilds objects by calling code named inside the file, and a deliberately crafted .dat can abuse that.

One dump, one load. This is the rule the whole chapter turns on. pickle.dump() writes exactly one object and pickle.load() reads exactly one object back. Three dumps need three loads. Nothing in the file records how many objects it holds, and there is no pickle.load_all(). So how do you know when to stop?

import pickle

f = open("three.dat", "wb")
pickle.dump([1101, "Aarav"], f)
pickle.dump([1102, "Diya"], f)
pickle.dump([1103, "Kabir"], f)
f.close()

print("--- WRONG: one load() only reads ONE record ---")
f = open("three.dat", "rb")
print(pickle.load(f))
f.close()

print("--- WRONG: loop with no guard ---")
f = open("three.dat", "rb")
try:
    while True:
        print(pickle.load(f))
except EOFError as e:
    print("EOFError caught. repr(e) =", repr(e))
f.close()

print("--- RIGHT: try/except EOFError is the stop signal ---")
f = open("three.dat", "rb")
try:
    while True:
        rec = pickle.load(f)
        print("Roll", rec[0], "->", rec[1])
except EOFError:
    pass
f.close()
--- WRONG: one load() only reads ONE record ---
[1101, 'Aarav']
--- WRONG: loop with no guard ---
[1101, 'Aarav']
[1102, 'Diya']
[1103, 'Kabir']
EOFError caught. repr(e) = EOFError('Ran out of input')
--- RIGHT: try/except EOFError is the stop signal ---
Roll 1101 -> Aarav
Roll 1102 -> Diya
Roll 1103 -> Kabir

Read the middle block again. The loop printed all three records correctly and then crashed. EOFError('Ran out of input') is not a bug in your program — it is the file telling you it is finished. Note that load() does not return None at the end; if it did, an unguarded while True would spin forever. The exception is the design. Every read loop in this chapter therefore looks the same: while True inside try, with except EOFError to stop.

One last trap: dump() and load() depend on the file mode you opened with, and getting it wrong fails loudly rather than silently.

f = open("w.dat", "rb")
try:
    pickle.dump("X", f)
except Exception as e:
    print(type(e).__name__, ":", e)
f.close()

f = open("w.dat", "wb")
try:
    pickle.load(f)
except Exception as e:
    print(type(e).__name__, ":", e)
f.close()
UnsupportedOperation : write
UnsupportedOperation : read
Import the module import pickle Standard library, nothing to install. Forgetting this line is the most common single-mark loss on this topic.
Write one record pickle.dump(obj, f) Writes one object into a file opened in wb, wb+, ab, ab+ or rb+. Returns None. Writing to an rb file raises io.UnsupportedOperation: write.
Read one record rec = pickle.load(f) Reads and returns the next object from a file opened in rb, rb+, wb+ or ab+. Raises EOFError when nothing is left.
End-of-file guard except EOFError: The only way to know a pickled file has ended. Put the f.close() inside this block so the file always closes.
Pickle in memory b = pickle.dumps(obj) The s versions work on a bytes object instead of a file. len(pickle.dumps(rec)) tells you how many bytes a record will occupy on disk.
Unpickle from memory obj = pickle.loads(b) Rebuilds the object from a bytes object. Types are preserved: a tuple comes back a tuple, a dict a dict.
Remember
  • Pickling turns a Python object into a byte stream; unpickling turns it back into a live object. The module is pickle and it is part of the standard library.
  • pickle.dump(obj, f) writes exactly ONE object and returns None. pickle.load(f) reads and returns the NEXT one. Three dumps need three loads.
  • Nothing in the file stores a record count, so the read loop must be while True inside try, ended by except EOFError.
  • load() raises EOFError('Ran out of input') at the end — it does not return None. That exception is the stop signal, not a mistake in your code.
  • Pickle is not encryption. The strings sit in the byte stream in plain view and any Python program can unpickle the file, so never store a PIN or password this way — and never load() a .dat file you did not create.

Create, Write and Read a Records File

Quick answer Build the file with one pickle.dump() per record into a file opened "wb", then read it back with a while True loop that stops on EOFError — these two programs are the base every other operation in this chapter is built on.

Everything the syllabus asks for — search, append, update — is a variation on two programs. Learn these two properly and the rest is four extra lines each.

A record is one Python object: usually a list like [1101, "Aarav", 95.5] or a dictionary like {"roll": 1101, "name": "Aarav", "marks": 95.5}. CBSE papers use both. Dictionaries are safer in an exam because you index by name (s["marks"]) instead of by position (s[2]), so you cannot silently pick the wrong field.

import pickle, os

def create_file():
    students = [
        {"roll": 1101, "name": "Aarav Sharma",  "cls": "XII-A", "marks": 95.5},
        {"roll": 1102, "name": "Diya Nair",     "cls": "XII-A", "marks": 88.0},
        {"roll": 1103, "name": "Kabir Singh",   "cls": "XII-B", "marks": 72.5},
        {"roll": 1104, "name": "Meera Iyer",    "cls": "XII-B", "marks": 91.0},
    ]
    f = open("student.dat", "wb")
    for s in students:
        pickle.dump(s, f)
    f.close()
    print("File created with", len(students), "records")

def read_file():
    f = open("student.dat", "rb")
    print("Roll   Name            Class   Marks")
    try:
        while True:
            s = pickle.load(f)
            print(s["roll"], " ", s["name"].ljust(15), s["cls"], " ", s["marks"])
    except EOFError:
        f.close()
    print("End of file reached")

create_file()
read_file()
print("size:", os.path.getsize("student.dat"), "bytes")
File created with 4 records
Roll   Name            Class   Marks
1101   Aarav Sharma    XII-A   95.5
1102   Diya Nair       XII-A   88.0
1103   Kabir Singh     XII-B   72.5
1104   Meera Iyer      XII-B   91.0
End of file reached
size: 310 bytes

Three details worth pausing on. The write loop calls dump() once per record — four records, four calls. The read loop has no idea there are four; it just keeps loading until EOFError fires, and f.close() sits inside the except block so the file closes exactly once, on the way out. And 310 bytes for four small records is more than you might expect: pickle stores every dictionary key as well as its value, so "roll", "name", "cls" and "marks" are each written four times. The same four records stored as lists are noticeably smaller.

import pickle, os

lists = [[1101, "Aarav Sharma", "XII-A", 95.5],
         [1102, "Diya Nair",    "XII-A", 88.0],
         [1103, "Kabir Singh",  "XII-B", 72.5],
         [1104, "Meera Iyer",   "XII-B", 91.0]]

f = open("as_lists.dat", "wb")
for r in lists:
    pickle.dump(r, f)
f.close()
print("same four records as lists:", os.path.getsize("as_lists.dat"), "bytes")
same four records as lists: 198 bytes

198 bytes against 310. The repeated key names are the entire difference. Dictionaries still win in an exam, because a named field cannot be confused with its neighbour, but this is where the extra bytes go.

Warning about "wb". create_file() opens with "wb", which erases whatever was there. That is correct here — the job is to create the file. It becomes a disaster only when you use "wb" where you meant "ab", which is the subject of the next section.

The other file layout. Instead of one dump() per record, you can put the whole list of records into a single dump().

import pickle

f = open("all_at_once.dat", "wb")
pickle.dump([1101, 1102, 1103], f)
f.close()

f = open("all_at_once.dat", "rb")
data = pickle.load(f)
f.close()
print("whole list in one load():", data, "| records:", len(data))
whole list in one load(): [1101, 1102, 1103] | records: 3

One load() returned the entire list, no EOFError loop needed. That looks easier, but it costs you elsewhere.

One dump() per recordOne dump() of the whole list
WritingA loop of dump() callsA single dump(records, f)
Readingwhile True with except EOFErrorA single pickle.load(f)
Appending one recordOpen in "ab", one dump() — cheapLoad the list, append, rewrite the whole file
Memory usedOne record at a timeThe entire file in RAM at once
CBSE exam patternYes, use thisRare

Because the syllabus names append as its own operation, it assumes the first layout. Write one dump() per record unless a question explicitly hands you a list.

Create or overwrite f = open("student.dat", "wb") Creates the file. If it already exists, every byte in it is erased at the moment of opening.
Write one record pickle.dump(rec, f) Call it once per record inside the for loop. Four records means four calls.
Read loop skeleton while True: rec = pickle.load(f) Must sit inside try with except EOFError, otherwise the program crashes when the file runs out.
Collect every record recs.append(pickle.load(f)) Builds a Python list of all records in memory. This is step one of any update program.
Whole file in one object pickle.dump(list_of_recs, f) Reading becomes a single pickle.load(f) with no EOFError loop, but appending then requires rewriting the whole file.
File size on disk os.path.getsize("student.dat") Returns bytes as an int. Needs import os. Four dict records of four fields came to 310 bytes; the same records as lists came to 198.
Remember
  • To create: open in "wb", loop over the records calling pickle.dump(rec, f) once each, then close. "wb" erases any existing file first, which is what create means.
  • To read: open in "rb" and run while True: rec = pickle.load(f) inside try, with except EOFError to stop. Put f.close() inside the except block.
  • A record can be a list or a dictionary; CBSE uses both. Dictionaries are safer under exam pressure because fields are named, not numbered.
  • Four four-field dictionary records came to 310 bytes, because pickle writes every key name alongside every value. The same four records written as lists came to 198 bytes.
  • Dumping the whole list in one call makes reading a single load(), but then appending one record means rewriting the entire file — so the exam pattern is one dump per record.

Search and Append

Quick answer Searching is the standard read loop with an if inside it — break early only when the key is unique — and appending is a single dump() into a file opened "ab", never "wb".

A binary file has no index. There is no way to jump to "record number 3" without reading records 1 and 2 first, because you do not know how many bytes they occupy until you unpickle them. So every search starts at the first record and walks forward. That is simply what the format allows.

There are two shapes of search question, and mixing them up costs marks.

  • Search by a unique key (roll number, employee id). At most one record can match, so break as soon as you find it.
  • Search by a condition (marks above 90, destination Delhi). Many records can match, so do not break — keep going and count the matches.

Both need a found flag or a counter, because if nothing matches you must print a message. Board answers routinely lose a mark for skipping that.

import pickle, os

def build():
    students = [
        {"roll": 1101, "name": "Aarav Sharma",  "cls": "XII-A", "marks": 95.5},
        {"roll": 1102, "name": "Diya Nair",     "cls": "XII-A", "marks": 88.0},
        {"roll": 1103, "name": "Kabir Singh",   "cls": "XII-B", "marks": 72.5},
        {"roll": 1104, "name": "Meera Iyer",    "cls": "XII-B", "marks": 91.0},
    ]
    f = open("student.dat", "wb")
    for s in students:
        pickle.dump(s, f)
    f.close()

def show():
    f = open("student.dat", "rb")
    try:
        while True:
            s = pickle.load(f)
            print("   ", s["roll"], s["name"], s["marks"])
    except EOFError:
        f.close()

def search(r):
    f = open("student.dat", "rb")
    found = False
    try:
        while True:
            s = pickle.load(f)
            if s["roll"] == r:
                print("FOUND:", s)
                found = True
                break
    except EOFError:
        pass
    f.close()
    if not found:
        print("Roll", r, "not found")

def toppers(cut):
    f = open("student.dat", "rb")
    c = 0
    try:
        while True:
            s = pickle.load(f)
            if s["marks"] > cut:
                print("  ", s["name"], "->", s["marks"])
                c += 1
    except EOFError:
        f.close()
    print("  matches:", c)

def append(rec):
    f = open("student.dat", "ab")
    pickle.dump(rec, f)
    f.close()
    print("Appended", rec["name"])

build()
print("Search roll 1103:"); search(1103)
print("Search roll 1199:"); search(1199)
print("Students above 90:"); toppers(90)
append({"roll": 1105, "name": "Rohan Verma", "cls": "XII-C", "marks": 84.0})
print("File now:"); show()
Search roll 1103:
FOUND: {'roll': 1103, 'name': 'Kabir Singh', 'cls': 'XII-B', 'marks': 72.5}
Search roll 1199:
Roll 1199 not found
Students above 90:
   Aarav Sharma -> 95.5
   Meera Iyer -> 91.0
  matches: 2
Appended Rohan Verma
File now:
    1101 Aarav Sharma 95.5
    1102 Diya Nair 88.0
    1103 Kabir Singh 72.5
    1104 Meera Iyer 91.0
    1105 Rohan Verma 84.0

Notice the small structural difference between the two search functions. In search() the break jumps out of the loop before EOFError is ever raised, so f.close() has to sit after the try block — putting it only inside except EOFError would leave the file open on a successful find. In toppers() there is no break, so the loop always ends with EOFError and closing inside the except is fine. Both patterns are correct; just be consistent about where the file gets closed.

Append. The whole operation is one line of difference from writing: open in "ab" instead of "wb". In append mode the file is created if it does not exist, every existing record is kept, and each dump() is placed at the end. You do not need to seek() anywhere — that is what the mode is for.

Now the trap. Type "wb" where you meant "ab" and the file is gone before you have written anything.

f = open("student.dat", "wb")
pickle.dump({"roll": 1106, "name": "Sneha Rao", "cls": "XII-C", "marks": 79.0}, f)
f.close()
print("After opening in 'wb' by mistake:"); show()
print("size:", os.path.getsize("student.dat"), "bytes")
After opening in 'wb' by mistake:
    1106 Sneha Rao 79.0
size: 76 bytes

Five records became one, and the file shrank to 76 bytes. Nothing raised an error and nothing warned you — the truncation happens at open(), before dump() even runs. There is no undo.

Open for append f = open("student.dat", "ab") Creates the file if missing, keeps all existing records, and puts every dump() at the end. No seek() needed.
Append one record pickle.dump(new_rec, f) One call adds one record. Open in ab, dump, close — that is the entire append operation.
Search on a unique key if rec["roll"] == key: found = True; break break is correct only when at most one record can match. Close the file after the try block in this pattern.
Search on a condition if rec["marks"] > cut: count += 1 No break here — several records may qualify. Report count at the end; if it is 0, print a not-found message.
Not-found message if not found: print("Record not found") Required by CBSE marking schemes. A search program without it is incomplete even if the search logic is right.
The truncation trap f = open("student.dat", "wb") Wipes the file the instant it is called, before any dump() runs. Use ab to add records; wb only to create fresh.
Remember
  • A binary file has no index, so a search always starts at the first record and moves forward one record at a time until it matches or hits EOFError.
  • Use break only when the key is unique, such as a roll number. For a condition like marks above 90, do not break — count the matches instead.
  • Always keep a found flag or a counter and print a not-found message when it stays False or zero. Board schemes award a mark for it.
  • If your search can break out of the loop, close the file after the try block, not only inside except EOFError, or a successful find leaves the file open.
  • Append means open in "ab" and dump once. Typing "wb" by mistake truncated a five-record file to one record and 76 bytes, silently, at open() time.

Update: Read, Change in Memory, Rewrite

Quick answer A pickled record cannot generally be edited where it lies, because a changed record rarely occupies the same number of bytes — so the reliable update is to read every record into a list, change it there, and write the whole list back.

Update is the operation students get wrong most often, and the reason is a single fact about pickle: a record's size on disk depends on the data inside it. Change the data and the size changes.

import pickle

a = {"roll": 1102, "name": "Diya Nair",       "cls": "XII-A", "marks": 88.0}
b = {"roll": 1102, "name": "Diya Nair Menon", "cls": "XII-A", "marks": 88.0}
print("len(pickle of 'Diya Nair')      :", len(pickle.dumps(a)))
print("len(pickle of 'Diya Nair Menon'):", len(pickle.dumps(b)))
len(pickle of 'Diya Nair')      : 76
len(pickle of 'Diya Nair Menon'): 82

Six extra characters in the name cost six extra bytes. If you tried to write the 82-byte version over the 76-byte one, it would run six bytes into whatever follows. So there is no general "edit this record in place" — the file is a stream of variable-length records, not a table of fixed-width rows.

The method to write in the exam is three steps: read everything into a list, change it in memory, rewrite the whole file.

import pickle

def build():
    students = [
        {"roll": 1101, "name": "Aarav Sharma", "cls": "XII-A", "marks": 95.5},
        {"roll": 1102, "name": "Diya Nair",    "cls": "XII-A", "marks": 88.0},
        {"roll": 1103, "name": "Kabir Singh",  "cls": "XII-B", "marks": 72.5},
        {"roll": 1104, "name": "Meera Iyer",   "cls": "XII-B", "marks": 91.0},
    ]
    f = open("student.dat", "wb")
    for s in students:
        pickle.dump(s, f)
    f.close()

def show(tag):
    print(tag)
    f = open("student.dat", "rb")
    try:
        while True:
            s = pickle.load(f)
            print("   ", s["roll"], s["name"].ljust(14), s["marks"])
    except EOFError:
        f.close()

def update_rewrite(roll, new_marks):
    recs = []
    f = open("student.dat", "rb")
    try:
        while True:
            recs.append(pickle.load(f))
    except EOFError:
        f.close()

    done = False
    for s in recs:
        if s["roll"] == roll:
            print("   old marks:", s["marks"])
            s["marks"] = new_marks
            print("   new marks:", s["marks"])
            done = True

    if done:
        f = open("student.dat", "wb")
        for s in recs:
            pickle.dump(s, f)
        f.close()
        print("   record updated")
    else:
        print("   roll", roll, "not found")

build()
show("BEFORE:")
print("Update roll 1103 -> 80.0 (rewrite method)")
update_rewrite(1103, 80.0)
show("AFTER:")
BEFORE:
    1101 Aarav Sharma   95.5
    1102 Diya Nair      88.0
    1103 Kabir Singh    72.5
    1104 Meera Iyer     91.0
Update roll 1103 -> 80.0 (rewrite method)
   old marks: 72.5
   new marks: 80.0
   record updated
AFTER:
    1101 Aarav Sharma   95.5
    1102 Diya Nair      88.0
    1103 Kabir Singh    80.0
    1104 Meera Iyer     91.0

The "wb" in the middle of that function looks alarming after the warning in the last section, but it is deliberate and safe here: every record is already sitting in recs in memory, so wiping the file and dumping the full list back loses nothing. It is safe only because you read everything first. A common board variant writes to a second file, temp.dat, instead of overwriting the original — same idea, and you copy the unchanged records across too.

The silent failure. Half of all broken update programs look like this.

import pickle

f = open("t4.dat", "wb")
pickle.dump({"name": "Ira", "marks": 50}, f)
f.close()

f = open("t4.dat", "rb")
d = pickle.load(f)
f.close()

d["marks"] = 99

f = open("t4.dat", "rb")
print(pickle.load(f))
f.close()
{'name': 'Ira', 'marks': 50}

The marks are still 50. load() gave a copy in RAM; changing d changed only that copy. Nothing raised an error, nothing warned you — the file simply never changed, because you never called dump() again. The file changes only when you write to it.

The rb+ and seek() method. There is one case where in-place editing works: when the new record pickles to exactly the same number of bytes as the old one. Replacing one float with another float does this, since every float takes the same space.

build()
f = open("student.dat", "rb+")
try:
    while True:
        pos = f.tell()
        s = pickle.load(f)
        if s["roll"] == 1102:
            before = len(pickle.dumps(s))
            s["marks"] = 92.0
            after = len(pickle.dumps(s))
            print("   record starts at byte", pos)
            print("   old pickled size:", before, " new pickled size:", after)
            f.seek(pos)
            pickle.dump(s, f)
            break
except EOFError:
    pass
f.close()
show("AFTER seek-update:")
   record starts at byte 79
   old pickled size: 76  new pickled size: 76
AFTER seek-update:
    1101 Aarav Sharma   95.5
    1102 Diya Nair      92.0
    1103 Kabir Singh    72.5
    1104 Meera Iyer     91.0

The key move is calling f.tell() before load(), so pos holds the byte where this record begins. After load() the position has moved past it, and f.seek(pos) rewinds. Both sizes came out 76, so the overwrite fit exactly.

What happens when it does not fit. Here the same trick is used with a longer name.

import pickle, os

if os.path.exists("bad.dat"):
    os.remove("bad.dat")

for s in [{"roll": 1101, "name": "Aarav Sharma", "cls": "XII-A", "marks": 95.5},
          {"roll": 1102, "name": "Diya Nair",    "cls": "XII-A", "marks": 88.0},
          {"roll": 1103, "name": "Kabir Singh",  "cls": "XII-B", "marks": 72.5}]:
    f = open("bad.dat", "ab")
    pickle.dump(s, f)
    f.close()
print("original size:", os.path.getsize("bad.dat"))

f = open("bad.dat", "rb+")
pos = f.tell()
rec = pickle.load(f)
pos = f.tell()
rec = pickle.load(f)
rec["name"] = "Diya Nair Menon"
f.seek(pos)
pickle.dump(rec, f)
f.close()
print("size after in-place write:", os.path.getsize("bad.dat"))

print("Now read the file back:")
f = open("bad.dat", "rb")
try:
    while True:
        print("   ", pickle.load(f))
except EOFError:
    print("   (EOF)")
except Exception as e:
    print("   CORRUPT ->", type(e).__name__, ":", e)
f.close()
original size: 233
size after in-place write: 233
Now read the file back:
    {'roll': 1101, 'name': 'Aarav Sharma', 'cls': 'XII-A', 'marks': 95.5}
    {'roll': 1102, 'name': 'Diya Nair Menon', 'cls': 'XII-A', 'marks': 88.0}
   CORRUPT -> UnpicklingError : invalid load key, '\x00'.

Read that output carefully, because it is the whole argument in one screen. The write appeared to succeed — no exception, and the file is still 233 bytes. The first record reads fine. The second reads back with its new name, so it even looks like it worked. Then the third record is gone: the 82-byte version ate six bytes of it, and pickle.UnpicklingError: invalid load key is what a half-eaten record looks like. The damage was invisible until someone tried to read the file.

So: use rb+ with seek() only when you can guarantee the size is unchanged. In every other case — and in your exam answer unless the question specifically demands otherwise — read all, change in memory, rewrite. The same read-change-rewrite pattern is what you use if a record has to be removed instead of edited: simply do not dump the unwanted record when writing the list back.

Open for read and write f = open("student.dat", "rb+") The file must already exist or you get FileNotFoundError. Position starts at byte 0 and both reading and writing are allowed.
Mark a record's start pos = f.tell() Must be called BEFORE pickle.load(), because load() moves the position past the record it just read.
Overwrite in place f.seek(pos); pickle.dump(rec, f) Safe ONLY if the edited record pickles to exactly the same number of bytes, for example one float replaced by another float.
Measure a record n = len(pickle.dumps(rec)) The exact bytes this record will take. Compare before and after editing to know whether an in-place write is safe.
The safe rewrite f = open("student.dat", "wb") Used AFTER every record is already in a Python list. Dump the full list back, changed records included.
Corruption symptom pickle.UnpicklingError Message reads 'invalid load key'. It means an earlier in-place write overran the following record. It appears only when you next read the file.
Remember
  • A pickled record's size depends on its data: the same dict with 'Diya Nair' takes 76 bytes and with 'Diya Nair Menon' takes 82, so in-place editing is not generally possible.
  • The safe update is three steps — read every record into a list, change the ones you want in memory, reopen in "wb" and dump the whole list back. Using "wb" is safe here only because you already read everything.
  • Changing the dictionary returned by load() does nothing to the file. The file changes only when you call dump() again.
  • The rb+ method needs pos = f.tell() BEFORE load(), then f.seek(pos) before dumping — and it is safe only when the new record pickles to exactly the same number of bytes.
  • A bad in-place write raises no error at the time. The file reads the first records normally and then raises pickle.UnpicklingError: invalid load key on the record that was overrun.

The formula sheet

Every formula in this chapter, in one place — screenshot it before your exam.

f = open("student.dat", "rb")
Open a binary file
f.close()
Close a file
with open("student.dat", "rb") as f:
Auto-close with a block
pos = f.tell()
Current byte position
f.seek(offset)
Move the position
data = f.read()
Read raw bytes
import pickle
Import the module
pickle.dump(obj, f)
Write one record
rec = pickle.load(f)
Read one record
except EOFError:
End-of-file guard
b = pickle.dumps(obj)
Pickle in memory
obj = pickle.loads(b)
Unpickle from memory
f = open("student.dat", "wb")
Create or overwrite
pickle.dump(rec, f)
Write one record
while True: rec = pickle.load(f)
Read loop skeleton
recs.append(pickle.load(f))
Collect every record
pickle.dump(list_of_recs, f)
Whole file in one object
os.path.getsize("student.dat")
File size on disk
f = open("student.dat", "ab")
Open for append
pickle.dump(new_rec, f)
Append one record
if rec["roll"] == key: found = True; break
Search on a unique key
if rec["marks"] > cut: count += 1
Search on a condition
if not found: print("Record not found")
Not-found message
f = open("student.dat", "wb")
The truncation trap
f = open("student.dat", "rb+")
Open for read and write
pos = f.tell()
Mark a record's start
f.seek(pos); pickle.dump(rec, f)
Overwrite in place
n = len(pickle.dumps(rec))
Measure a record
f = open("student.dat", "wb")
The safe rewrite
pickle.UnpicklingError
Corruption symptom

Test yourself

Tap an answer to check it instantly — you'll see why it's right, and what to revise if it isn't.

0 correct · 0/12 answered
Q1

Predict the output. import pickle f = open("t1.dat", "wb") pickle.dump([10, 20], f) pickle.dump([30, 40], f) f.close() f = open("t1.dat", "rb") x = pickle.load(f) f.close() print(x[0] + x[1])

Q2

Predict the output. import pickle, os f = open("t2.dat", "wb") pickle.dump({"a": 1}, f) f.close() f = open("t2.dat", "wb+") print(os.path.getsize("t2.dat"), f.read()) f.close()

Q3

Predict the output. import pickle f = open("t3.dat", "wb") for i in [11, 22, 33, 44, 55]: pickle.dump(i, f) f.close() f = open("t3.dat", "rb") c = 0 t = 0 try: while True: v = pickle.load(f) c += 1 t += v except EOFError: f.close() print(c, t)

Q4

Predict the output. import pickle f = open("t4.dat", "wb") pickle.dump({"name": "Ira", "marks": 50}, f) f.close() f = open("t4.dat", "rb") d = pickle.load(f) f.close() d["marks"] = 99 f = open("t4.dat", "rb") print(pickle.load(f)) f.close()

Q5

Predict the output. import pickle f = open("t5.dat", "wb") pickle.dump("Chennai", f) f.close() f = open("t5.dat", "ab+") print(f.tell(), f.read()) f.close()

Q6

Predict the output. import pickle t = (1, "Ravi", [2, 3]) f = open("t6.dat", "wb") pickle.dump(t, f) f.close() f = open("t6.dat", "rb") b = pickle.load(f) f.close() print(b, type(b))

Q7

Predict the output. import pickle f = open("t7.dat", "wb") for i in [11, 22, 33]: pickle.dump(i, f) f.close() f = open("t7.dat", "rb") print(pickle.load(f), pickle.load(f)) f.seek(0) print(pickle.load(f)) f.close()

Q8

Predict the output. import pickle f = open("t8.dat", "wb") print(pickle.dump([1, 2], f)) f.close()

Q9

You need to add new records to an existing binary file and also read the old records back in the same session, without losing anything already stored. Which mode should you open it in?

Q10

A binary file holds 5 records, each written with a separate pickle.dump(). The file is opened in "rb" and pickle.load() is called six times, with no error handling. What happens on the 6th call?

Q11

Why can a pickled record usually not be updated in place using f.seek() followed by another pickle.dump()?

Q12

Rhea stores customer UPI details in a pickled binary file and says the data is safe because 'the file is binary, so nobody can read it'. What is the correct assessment?

NCERT solutions & previous-year questions

Step-by-step model answers — tap a question to reveal the full solution.

NCERT questions 6

1 Differentiate between a text file and a binary file.Text vs binary files
PointText fileBinary file
Stored asCharacters, encoded (usually UTF-8) into bytesRaw bytes exactly as the object occupies them
Mode used"r", "w", "a""rb", "wb", "ab" and their + forms
read() returnsA strA bytes object
Line endingsTranslated on Windows, so "\n" is stored as "\r\n"No translation whatsoever
Human readableYes, in NotepadNo, it looks like garbage
Typical useNotes, CSV, .py source, logsImages, .exe, pickled records

The decisive difference is the line-ending translation, and it is easy to see:

import os

f = open("marks.txt", "w")
f.write("Aarav,95\nDiya,88\n")
f.close()

f = open("marks.txt", "r")
print("TEXT mode :", repr(f.read()))
f.close()

f = open("marks.txt", "rb")
print("BINARY mode:", f.read())
print("size on disk:", os.path.getsize("marks.txt"), "bytes")
f.close()

Output (on Windows):

TEXT mode : 'Aarav,95\nDiya,88\n'
BINARY mode: b'Aarav,95\r\nDiya,88\r\n'
size on disk: 19 bytes

The string is 17 characters long but the file is 19 bytes, because text mode replaced each newline with a carriage-return-and-newline pair. For a picture or a pickled record that silent substitution would corrupt the data, which is precisely why such files must be opened in binary mode.

2 What is the difference between the file modes 'wb' and 'ab'? Which one would you use to add a new record to an existing binary file, and why?Binary file modes

Difference. Both create the file if it does not exist, and neither can read. They differ in what happens to data that is already there.

  • "wb"write mode. The file is truncated to zero bytes the moment open() is called. Everything previously stored is lost. The position starts at byte 0.
  • "ab"append mode. Existing data is kept. The position starts at the end of the file and every write lands at the end, whatever you do with seek().

Which one to use. Use "ab" to add a record. It preserves the existing records and needs no seeking. Use "wb" only when you intend to create the file from scratch, or when you are rewriting a file whose contents you have already read into memory.

import pickle, os

f = open("student.dat", "wb")
for s in [{"roll": 1101, "name": "Aarav Sharma"},
          {"roll": 1102, "name": "Diya Nair"}]:
    pickle.dump(s, f)
f.close()

f = open("student.dat", "ab")
pickle.dump({"roll": 1103, "name": "Kabir Singh"}, f)
f.close()

f = open("student.dat", "rb")
try:
    while True:
        print("  ", pickle.load(f))
except EOFError:
    f.close()

Output:

   {'roll': 1101, 'name': 'Aarav Sharma'}
   {'roll': 1102, 'name': 'Diya Nair'}
   {'roll': 1103, 'name': 'Kabir Singh'}

Had the third record been added with "wb" instead of "ab", the first two records would have been erased before it was written and only Kabir Singh would remain. Nothing warns you, because the truncation happens inside open(), before dump() ever runs.

3 What is pickling and unpickling? Name the module used for it in Python and the two functions it provides for writing to and reading from a binary file.pickle module

Pickling (also called serialisation) is the process of converting a Python object — a list, a dictionary, a tuple, a number, a string — into a stream of bytes so that it can be stored in a binary file.

Unpickling (deserialisation) is the reverse: reading that byte stream back and reconstructing the original Python object, with its type intact.

Module: pickle, part of the Python standard library. It is brought in with import pickle.

The two functions:

  • pickle.dump(object, fileobject) — pickles one object and writes it to a file opened in a binary write mode. Returns None.
  • pickle.load(fileobject) — reads the next pickled object from a file opened in a binary read mode and returns it. Raises EOFError when the file has no more objects.
import pickle

stu = {"roll": 1101, "name": "Aarav Sharma", "marks": 95.5}

f = open("one.dat", "wb")
pickle.dump(stu, f)
f.close()

f = open("one.dat", "rb")
back = pickle.load(f)
f.close()

print("loaded    :", back)
print("type      :", type(back))
print("same value:", back == stu)
print("same object:", back is stu)
print("marks+5   :", back["marks"] + 5)

Output:

loaded    : {'roll': 1101, 'name': 'Aarav Sharma', 'marks': 95.5}
type      : 
same value: True
same object: False
marks+5   : 100.5

Two points to note. The object comes back as a real dictionary, so ordinary arithmetic on its fields works. And back is stu is False — unpickling produces a fresh copy, not a reference to the original object. Finally, remember that pickling is not encryption: the strings are plainly visible inside the stored bytes.

4 Write a Python program to create a binary file with roll number and name. Search for a given roll number and display the corresponding name; if the roll number is not found, display an appropriate message.Search in a binary file
import pickle

f = open("roll.dat", "wb")
for r in [[11, "Aditi Bose"], [12, "Bharat Malhotra"], [13, "Chetna Gill"]]:
    pickle.dump(r, f)
f.close()

def search(rno):
    f = open("roll.dat", "rb")
    found = False
    try:
        while True:
            rec = pickle.load(f)
            if rec[0] == rno:
                print("Name:", rec[1])
                found = True
                break
    except EOFError:
        pass
    f.close()
    if not found:
        print("Rollno not found")

search(12)
search(99)

Output:

Name: Bharat Malhotra
Rollno not found

Points that earn the marks. The file is created with one pickle.dump() per record. The search reads it back with while True guarded by except EOFError, since there is no way to know in advance how many records there are. A roll number is unique, so break is correct once a match is found. Because that break jumps out before EOFError is ever raised, f.close() is placed after the try block rather than inside the except — otherwise a successful search would leave the file open. The found flag drives the not-found message, which a complete answer must have.

5 Write a Python program to create a binary file with roll number, name and marks, and then update the marks of a roll number entered by the user.Update a binary file
import pickle

f = open("marks.dat", "wb")
for r in [[11, "Aditi Bose", 55], [12, "Bharat Malhotra", 61], [13, "Chetna Gill", 78]]:
    pickle.dump(r, f)
f.close()

def update_marks(rno, newm):
    recs = []
    f = open("marks.dat", "rb")
    try:
        while True:
            recs.append(pickle.load(f))
    except EOFError:
        f.close()

    found = False
    for rec in recs:
        if rec[0] == rno:
            rec[2] = newm
            found = True

    if found:
        f = open("marks.dat", "wb")
        for rec in recs:
            pickle.dump(rec, f)
        f.close()
        print("Marks updated for rollno", rno)
    else:
        print("Rollno not found")

update_marks(12, 88)
f = open("marks.dat", "rb")
try:
    while True:
        print("  ", pickle.load(f))
except EOFError:
    f.close()
update_marks(50, 90)

Output:

Marks updated for rollno 12
   [11, 'Aditi Bose', 55]
   [12, 'Bharat Malhotra', 88]
   [13, 'Chetna Gill', 78]
Rollno not found

Why the file is rewritten rather than edited. Pickled records have variable length — a record's size on disk depends on the data inside it — so you cannot reliably write a changed record over the old one without spilling into the next record. The correct approach is three steps: read every record into a Python list, change the required record in memory, then reopen the file in "wb" and dump the whole list back. Opening with "wb" is safe here only because all the records are already held in recs. In a real program you would take the roll number and the new marks from input(); fixed values are used above so the output can be shown.

6 Why is it necessary to close a file after use? What is the advantage of using the with statement for file handling?close() and the with statement

Why closing matters. Python does not send every write() straight to the disk. Data is collected in a memory buffer and written out in blocks, because that is far faster. close() flushes that buffer, so calling it is what actually guarantees your records reach the disk. It also releases the operating-system file handle; a program that opens files in a loop without closing them can eventually run out of handles. And on some systems a file left open stays locked against other programs.

The advantage of with. A with block closes the file automatically on the way out — including when an exception is raised inside the block, which is exactly the case where a hand-written f.close() gets skipped.

import pickle

with open("m.dat", "wb") as f:
    pickle.dump({"city": "Pune", "pin": 411001}, f)
print("closed after the with-block?", f.closed)

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

Output:

closed after the with-block? True
{'city': 'Pune', 'pin': 411001}

The f.closed attribute is True immediately after the block ends, without any explicit close() call. Compare this with the manual style, where an exception between open() and close() would leave the file open and its buffer unflushed. One caution for this chapter: a pickled read loop ends by design with an EOFError, so if you use with for reading, keep the try and except EOFError inside the with block.

Previous-year board questions 4

Q1 A binary file 'STUDENT.DAT' has the structure [admission_number, Name, Percentage]. Write a function countrec() in Python that would read the contents of the file 'STUDENT.DAT' and display the details of those students whose percentage is above 75. Also display the number of students scoring above 75%. (3 marks) 2020 (Board)
import pickle

def countrec():
    f = open("STUDENT.DAT", "rb")
    count = 0
    try:
        while True:
            rec = pickle.load(f)
            if rec[2] > 75:
                print(rec[0], rec[1], rec[2])
                count += 1
    except EOFError:
        f.close()
    print("Number of students scoring above 75% =", count)
    return count

Tested against a file built from these records:

[2101, "Nisha Agarwal", 81.4]
[2102, "Rahul Yadav", 68.0]
[2103, "Sana Qureshi", 92.7]
[2104, "Tarun Bhatia", 75.0]
[2105, "Uma Reddy", 77.25]

Output:

2101 Nisha Agarwal 81.4
2103 Sana Qureshi 92.7
2105 Uma Reddy 77.25
Number of students scoring above 75% = 3

Marking points. One mark for opening in "rb" and looping with pickle.load(); one for the try with except EOFError, without which the function crashes at the end of the file; one for the condition, the counter and displaying the count. Note that Tarun Bhatia at exactly 75.0 is not displayed — the question says above 75, so the test is a strict comparison, not 'greater than or equal to'. There is no break here, because several records can satisfy the condition.

Q2 Aman has created a binary file record.dat storing records as a dictionary with keys 'Employee id', 'Name' and 'Salary'. He now has to update a record based on the employee id entered by the user and write the updated record to the file temp.dat. Records which are not updated must also be written to temp.dat. If the employee id is not found, an appropriate message should be displayed. Complete the code below. import _______ # Statement 1 def update(): fin = open("record.dat", "rb") fout = open(_________) # Statement 2 found = False eid = int(input("Enter employee id :: ")) while True: try: emp = pickle.load(fin) if emp["Employee id"] == eid: found = True emp["Salary"] = int(input("Enter new salary :: ")) pickle.__________ # Statement 3 else: pickle.dump(emp, fout) except: break if found == True: print("The salary of employee id", eid, "has been updated.") else: print("No employee with such id is found") fin.close() fout.close() (i) Name the module to be imported in Statement 1. (ii) Write the correct statement required in Statement 2. (iii) Write the statement required in Statement 3. (iv) Why must the records that are not updated also be written to temp.dat? (4 marks) 2023 (Board)

(i) pickle — so Statement 1 is import pickle.

(ii) fout = open("temp.dat", "wb") — the output file must be opened in binary write mode so that pickle.dump() can write into it.

(iii) pickle.dump(emp, fout) — after the salary has been changed in memory, the modified record must be written out, otherwise the updated employee would be missing from temp.dat altogether.

(iv) Because temp.dat is being built as a complete replacement for record.dat. A pickled record has a variable length, so a changed record cannot simply be written over the old one in place; the standard technique is to copy every record across, altering only the one that matches. Any record skipped during the copy is permanently lost.

The completed function, tested. The two input() calls are replaced by parameters so that the run is reproducible; in the exam, keep the input() calls exactly as the question gives them.

import pickle

def update(eid, newsal):
    fin = open("record.dat", "rb")
    fout = open("temp.dat", "wb")
    found = False
    while True:
        try:
            emp = pickle.load(fin)
            if emp["Employee id"] == eid:
                found = True
                emp["Salary"] = newsal
                pickle.dump(emp, fout)
            else:
                pickle.dump(emp, fout)
        except:
            break
    if found == True:
        print("The salary of employee id", eid, "has been updated.")
    else:
        print("No employee with such id is found")
    fin.close()
    fout.close()

Run on a file holding three employees, updating id 2 to a salary of 44000:

The salary of employee id 2 has been updated.
   {'Employee id': 1, 'Name': 'Neha Kulkarni', 'Salary': 42000}
   {'Employee id': 2, 'Name': 'Ajay Pillai', 'Salary': 44000}
   {'Employee id': 3, 'Name': 'Farah Sheikh', 'Salary': 51000}

One remark on style: the given skeleton uses a bare except: break. It works because load() raises EOFError at the end, but a bare except also swallows genuine bugs such as a wrong key name. In your own programs write except EOFError:.

Q3 Write a function in Python to search and display the details of all trains whose destination is 'Delhi', from a binary file 'TRAIN.DAT'. Assume that the binary file stores each record as a list containing [Train Number, Train Name, Train Destination]. (3 marks) Board pattern (repeatedly asked)
import pickle

def DelhiTrains():
    f = open("TRAIN.DAT", "rb")
    c = 0
    try:
        while True:
            t = pickle.load(f)
            if t[2] == "Delhi":
                print(t[0], t[1], t[2])
                c += 1
    except EOFError:
        f.close()
    if c == 0:
        print("No train found")

Tested against a file holding:

[12951, "Rajdhani Express", "Delhi"]
[12009, "Shatabdi Express", "Ahmedabad"]
[12615, "Grand Trunk Express", "Delhi"]
[11077, "Jhelum Express", "Pune"]

Output:

12951 Rajdhani Express Delhi
12615 Grand Trunk Express Delhi

Marking points. Open in "rb"; loop with pickle.load() inside try with except EOFError; test t[2] == "Delhi" and display the record. There must be no break — the destination is not a unique key, so several trains can match, and breaking after the Rajdhani would have hidden the Grand Trunk Express entirely. A counter is worth keeping so the function can report when nothing matched. Since the records are lists, the fields are addressed by position: t[0] is the number, t[1] the name and t[2] the destination.

Q4 A binary file 'ITEM.DAT' stores records of items sold in a school stationery shop as lists of the form [item_no, item_name, price]. Write user-defined functions in Python to: (i) AddItem() — accept a record and add it to the file without disturbing the records already stored. (2 marks) (ii) Costly() — read the file and display the details of all items priced above Rs 500, along with a count. If no such item exists, display a suitable message. (3 marks) Board pattern
import pickle

def AddItem(rec):
    f = open("ITEM.DAT", "ab")
    pickle.dump(rec, f)
    f.close()

def Costly():
    f = open("ITEM.DAT", "rb")
    c = 0
    try:
        while True:
            it = pickle.load(f)
            if it[2] > 500:
                print(it[0], it[1], it[2])
                c += 1
    except EOFError:
        f.close()
    if c == 0:
        print("No item found")
    return c

Tested by adding four items and then listing the costly ones:

for r in [[1, "Notebook", 60], [2, "Scientific Calculator", 1250],
          [3, "Geometry Box", 210], [4, "School Bag", 899]]:
    AddItem(r)
print("Items priced above Rs 500:")
print("count =", Costly())

Output:

Items priced above Rs 500:
2 Scientific Calculator 1250
4 School Bag 899
count = 2

Marking points for (i). The phrase 'without disturbing the records already stored' is the examiner pointing straight at the mode. It must be "ab". Writing "wb" would erase the whole file at open() and costs the marks even though the rest of the function is identical. "ab" also creates the file on the first call, so no separate creation step is needed.

Marking points for (ii). Open in "rb", loop under try with except EOFError, apply a strict comparison against 500, keep a counter, and print the not-found message when the counter stays at zero. No break, because price is a condition rather than a unique key.

Part of Priodemy for School

Interactive CBSE lessons, Class 8–12 — free with every school on Priodemy EduSuite. Explore more chapters and labs on the Priodemy for School hub.

Ask AI