Updating a Record
Amit's marks were entered wrongly and must be corrected. The record is in the middle of the file — there are four of them now, after the last lesson appended Neha — and this is the one operation where the obvious idea is the wrong one.
1Read them all, change one, write them all back
The same three steps as changing a text file, and for the same reason: records are different sizes, so there is no safe way to drop a new one into the middle.
records = [...]The reading loop from two lessons ago, collecting into a list. Then close the file.
r[2] = new_marksPlain list work. The file is not open and nothing has happened to it yet.
open(..., 'wb')'wb' empties the file, then every record is dumped again — the changed one among them.
import pickle
roll = int(input('Roll number to update : '))
new_marks = int(input('New marks : '))
# 1. read every record into a list
f = open('student.dat', 'rb')
records = []
try:
while True:
records.append(pickle.load(f))
except EOFError:
pass
f.close()
# 2. change the one you want, in memory
found = False
for r in records:
if r[0] == roll:
r[2] = new_marks
found = True
# 3. write them all back
f = open('student.dat', 'wb')
for r in records:
pickle.dump(r, f)
f.close()
if found == True:
print('Record updated.')
else:
print('No record with roll number', roll)
f = open('student.dat', 'rb')
try:
while True:
print(pickle.load(f))
except EOFError:
pass
f.close()Roll number to update : 3 New marks : 72 Record updated. [1, 'Ravi', 78] [2, 'Meera', 91] [3, 'Amit', 72] [4, 'Neha', 88]
records.append(pickle.load(f))Step 1. After this loop the whole file is in an ordinary Python list, and the file itself has been closed.
for r in records: if r[0] == roll:Step 2. r is the record, so r[2] = new_marks changes the marks inside the list. Nothing on the disk has moved.
open('student.dat', 'wb')Step 3. This EMPTIES the file — which is exactly what you want, because the list in memory now holds the correct version of everything.
foundThe same flag as the search lesson. Without it, updating a roll number that does not exist rewrites the file and says nothing at all.
open(..., 'wb') and the last dump(), the only complete copy of your data is the list in memory. If the program crashed in there, the file would be left half written. For schoolwork that is fine; a real program writes a new file and renames it afterwards, exactly as the text-file chapter did.2The tempting wrong way: writing over it in place
You know seek() and tell(), and 'rb+' allows reading and writing through one handle. So why not note where the record started, change it, and write it straight back over itself?
import pickle
f = open('student.dat', 'rb+')
try:
while True:
position = f.tell()
record = pickle.load(f)
if record[0] == 3:
record[2] = 72
f.seek(position)
pickle.dump(record, f)
break
except EOFError:
pass
f.close()That program works. Change one number to another number of the same size and the new record takes exactly as many bytes as the old one, so it fits where it was. Now change the name instead:
import pickle
f = open('student.dat', 'rb+')
try:
while True:
position = f.tell()
record = pickle.load(f)
if record[0] == 3:
record[1] = 'Amitabh'
f.seek(position)
pickle.dump(record, f)
break
except EOFError:
pass
f.close()
f = open('student.dat', 'rb')
try:
while True:
print(pickle.load(f))
except EOFError:
pass
f.close()[1, 'Ravi', 78]
[2, 'Meera', 91]
[3, 'Amitabh', 65]
Traceback (most recent call last):
File "in_place_broken.py", line 20, in <module>
print(pickle.load(f))
^^^^^^^^^^^^^^
_pickle.UnpicklingError: invalid load key, '\x10'.'rb+' and seek() for knowing why the other way is dangerous.3Deleting a record is the same three steps
You never remove a record from a file. You write the file again without that record:
import pickle
f = open('student.dat', 'rb')
records = []
try:
while True:
records.append(pickle.load(f))
except EOFError:
pass
f.close()
kept = []
removed = 0
for r in records:
if r[0] == 2:
removed = removed + 1
else:
kept.append(r)
f = open('student.dat', 'wb')
for r in kept:
pickle.dump(r, f)
f.close()
print('Records removed:', removed)Records removed: 1
4Try it
The file is created first, so the program runs on its own. Change the roll number, or update the name instead of the marks:
5Recap
The three steps. Step 3 opens the file in 'wb', which empties it before the corrected records go in.
Between steps 1 and 3 the file is closed and the data is an ordinary Python list.
Otherwise an update of a roll number that is not there rewrites the file and reports nothing.
It only works while the new record pickles to exactly the same number of bytes.
The extra bytes land on the next record. UnpicklingError: invalid load key.
Build a list of the records you are keeping, and write that list back.
- 1
Update a student's name rather than their marks.
Hint · r[1] = new_name — and use the three-step version.
- 2
Give every student five extra marks in one run.
Hint · No if at all: change every record in the list, then write it back.
- 3
Delete a record by roll number, and report how many were removed.
Hint · Build the kept list; do not try to remove from the file.
- 4
Update a roll number that is not in the file and check the file afterwards.
Hint · Every record is still there — rewritten identically.
What are the three steps of updating a record in a binary file?
Why is writing over a record in place dangerous?
Which mode does step 3 use?
How do you delete a record?