For 9618 examinations in 2027–2029, file skills connect AS text-file work with A Level file organisation, random access and exception handling. Build the file model before choosing the open mode.
Content owner: Michael Print · Written for A-Level learners · Checked against official specifications
The idea to start with
READ obtains existing data; WRITE creates/replaces contents; APPEND adds after existing contents. EOF means no further data remains; a blank line is still a record of text. Close files reliably after use.
Serial organisation follows arrival order, sequential organisation sorts records by a key, and random organisation locates records using a key-to-location rule. Access method is separate: reading in order is sequential access; moving to a known record position is direct access.
Cambridge International 9618 · 9618 (2027–2029): 10.3, 13.2, 20.2; Paper 4 practical scope
Before you start
Useful foundations
Loops, functions and records
Integer keys and fixed-capacity indexing
By the end, you should be able to
Choose read/write/append modes and handle EOF
Compare serial, sequential and random organisation
Simulate random-file access with an explicit hash algorithm and a text-loaded data structure
Trace a direct record update as a theory extension
Use specific exceptions and preserve the requested testing evidence
Year and Paper 4 conventions
The baseline is 2027–2029 syllabus version 2. The checked 2026 version 2 has the same teaching requirements here; use the entry year’s own materials.
Paper 4 assesses practical application of sections 19–20, except low-level/declarative programming, using Python, Java or .NET Visual Basic in console mode. Tasks may request programs or program elements; submit their specified code and testing evidence.
General OCR project guidance does not replace Cambridge’s task instructions. Follow the exact filenames, record formats and evidence requirements supplied with each practical task.
Simulate random files using text and a data structure
Cambridge’s official clarification says supplied source files are text, rather than binary. Candidates are expected to write a hashing algorithm and access another data structure to simulate random files.
This practical example loads text into an array-backed hash table, performs keyed access and saves text for reloading. Using only a built-in dictionary would conceal the hashing algorithm being practised.
The later binary example is a theory extension for direct-access mechanics. It is separate from Cambridge’s practical text/ADT convention and from assessed learner work.
An update survives another program run only when saved
1
Load text records
Read key,quantity lines from the supplied text file.
2
Build the hash table
Calculate each key’s slot and resolve collisions using bounded linear probing.
3
Look up and update
Use the same probe sequence; confirm the stored key before accessing its quantity.
4
Save occupied records
Write the changed records to text. An in-memory change alone does not persist.
5
Reload next time
Rebuild the table by hashing the saved records; text line order is not a physical record address.
Choose a text mode and close reliably
Cambridge pseudocode uses OPENFILE ... FOR READ/WRITE/APPEND, READFILE, WRITEFILE and CLOSEFILE. EOF tests whether another line remains. Python r/w/a provide the corresponding basic text roles.
State the encoding where appropriate. Python’s with block closes its file even if an exception exits the block. Cambridge pseudocode still needs its explicit closing operation.
Choose the mode from the intended change
READ / r
Obtain existing records without replacing their contents.
WRITE / w
Create a file or replace existing contents: previous records are lost.
APPEND / a
Keep existing contents and add new records after them.
EOF and a blank line are different
Python iteration ends at EOF without an extra invalid read. readline() returns an empty string at EOF, but a blank text line normally returns a newline character.
Cambridge READFILE represents the line’s text without Python’s newline handling. Do not import Python’s blank/EOF representation into pseudocode. Validate record structure before converting fields; silently skipping malformed lines can hide corruption.
Separate organisation from access
Serial organisation keeps arrival order; a serial log can be read from start to finish. Sequential organisation sorts records by key, supporting sorted searches and merges. Random organisation maps a key to a location, often by hashing.
Access is a separate choice. Sequential access reads records in order; direct access seeks a known position without reading every earlier record. An index may supply direct positions even for an otherwise ordered file.
The deterministic hash key MOD 5 can collide. With five slots 0–4, 12 uses slot 2, 17 probes from 2 to 3, and 9 uses 4. Lookup 17 checks slots 2 then 3 and compares stored keys.
Linear probing wraps around and needs a bounded probe count when full. Deletion needs a tombstone or another collision-aware rule: making slot 2 simply empty could incorrectly stop a search for 17.
The practical table’s operating contract
Our list supplies fixed-capacity slots; modulo and bounded probing implement hashing. Put inserts or updates a key. Keys and quantities are non-negative integers; a repeated input key replaces its earlier quantity.
Insertion into a full table raises OverflowError. None marks an unused slot. No deletion is implemented, so lookup may safely stop at None; insertion and lookup follow the same probe sequence.
Saving writes occupied records as text. Reloading hashes them again, so their slot arrangement need not match saved line order. A keyed lookup avoids re-reading every earlier text line on each request.
Theory extension: record positions and byte offsets
Cambridge random-file pseudocode uses FOR RANDOM, SEEK, GETRECORD and PUTRECORD. PUTRECORD replaces data at the chosen pointer. State an exercise’s record-number convention; a record address is not automatically a Python byte offset.
In the binary theory example, slots are zero-based and records are eight bytes each. Slot s begins at byte 8×s. This demonstrates physical direct access; the text/ADT simulation follows the practical clarification.
The struct format >II stores key and quantity as two four-byte unsigned integers in fixed byte order. r+b opens an existing binary file for updating; wb creates the demonstration file and truncates existing contents.
Variable-length UTF-8 lines cannot safely use this fixed-offset formula. The demonstration uses a temporary folder, protecting a learner’s existing files from overwriting.
Exceptions make a failed operation explicit
An exception interrupts normal execution. Missing files can raise FileNotFoundError; non-numeric conversion can raise ValueError; failed device/file operations can raise an OSError subtype.
Catch failures the program can explain or recover from; otherwise let them surface. Catching every exception and returning zero falsely presents failed processing as a valid total.
Validation checks a field before use. Exception handling deals with failures during an attempted operation, including conditions changing between check and use. A partial read must not count as a complete fixed record.
The examples reject malformed records and retain the filename and line number for the caller. An interface can explain the failure without exposing unrelated private data.
Worked example
Read quantities and append another record
Records use item-code,quantity with one comma and an integer quantity at least zero; blank records are invalid. The initial total is 4+3=7. Appending a third line preserves the earlier records and gives total 12.
The code produces 7, 12, empty: 0, invalid record caught, and missing file caught. Malformed records, non-integer quantities and negative quantities raise ValueError with the filename and one-based line number. File iteration handles an empty file safely.
Runnable Python 3: text files and specific exceptionspython
from pathlib import Path
from tempfile import TemporaryDirectory
def total_quantity(path):
total = 0
with path.open("r", encoding="utf-8") as source:
for number, line in enumerate(source, start=1):
context = f"{path.name}: line {number}"
fields = line.strip().split(",")
if len(fields) != 2 or not fields[0]:
raise ValueError(f"{context}: invalid record")
try:
quantity = int(fields[1])
except ValueError as error:
raise ValueError(f"{context}: quantity must be an integer") from error
if quantity < 0:
raise ValueError(f"{context}: negative quantity")
total += quantity
return total
with TemporaryDirectory() as folder:
path = Path(folder) / "items.txt"
with path.open("w", encoding="utf-8") as target:
target.write("A,4\nB,3\n")
print(total_quantity(path))
with path.open("a", encoding="utf-8") as target:
target.write("C,5\n")
print(total_quantity(path))
path.write_text("", encoding="utf-8")
print("empty:", total_quantity(path))
path.write_text("A,no\n", encoding="utf-8")
try:
total_quantity(path)
except ValueError:
print("invalid record caught")
try:
total_quantity(Path(folder) / "missing.txt")
except FileNotFoundError:
print("missing file caught")
Worked example
Load text and simulate keyed random-file access
Load text records 12,4; 17,3; 9,5 into five slots. Key MOD 5 puts 12 in slot 2, probes 17 from 2 to 3, and puts 9 in 4.
The printed table is [None, None, (12, 4), (17, 3), (9, 5)]. Lookup 17 checks slots 2 and 3 and returns (17, 3).
Lookup 22 checks 2, 3, 4 and 0. Unused slot 0 proves absence, returning None. Every search is bounded by the capacity.
Update key 17 to quantity 8 in the same slot. Save occupied records to text and reload a new table: the final lookup gives (17, 8). No binary input or byte-seeking is needed.
Test empty input, collisions wrapping from 4 to 0, missing keys and a full table. The code deliberately does not implement deletion.
Collision trace for the original text records
Operation
Probe slots
Result
Insert 12
2
Slot 2: (12,4)
Insert 17
2,3
Slot 3: (17,3)
Insert 9
4
Slot 4: (9,5)
Find 22
2,3,4,0
Missing: slot 0 is unused
Update 17
2,3
Slot 3: (17,8)
Runnable Python 3: text-file input and an explicit array-backed hash tablepython
from pathlib import Path
from tempfile import TemporaryDirectory
def probe_slots(table, key):
if not table or type(key) is not int or key < 0:
raise ValueError("Expected a non-empty table and non-negative integer key")
start = key % len(table)
for offset in range(len(table)):
yield (start + offset) % len(table)
def put_record(table, key, quantity):
if type(quantity) is not int or quantity < 0:
raise ValueError("Expected a non-negative integer quantity")
for slot in probe_slots(table, key):
if table[slot] is None or table[slot][0] == key:
table[slot] = (key, quantity)
return
raise OverflowError("Hash table is full")
def get_record(table, key):
for slot in probe_slots(table, key):
if table[slot] is None:
return None
if table[slot][0] == key:
return table[slot]
return None
def load_records(path, capacity):
if type(capacity) is not int or capacity <= 0:
raise ValueError("Expected a positive integer capacity")
table = [None] * capacity
with path.open("r", encoding="utf-8") as source:
for number, line in enumerate(source, start=1):
fields = line.strip().split(",")
context = f"{path.name}: line {number}"
if len(fields) != 2:
raise ValueError(f"{context}: expected key,quantity")
try:
key, quantity = (int(field) for field in fields)
put_record(table, key, quantity)
except (ValueError, OverflowError) as error:
raise type(error)(f"{context}: {error}") from error
return table
def save_records(path, table):
with path.open("w", encoding="utf-8") as target:
for record in table:
if record is not None:
key, quantity = record
target.write(f"{key},{quantity}\n")
with TemporaryDirectory() as folder:
path = Path(folder) / "stock.txt"
path.write_text("12,4\n17,3\n9,5\n", encoding="utf-8")
table = load_records(path, 5)
print("slots:", table)
print("find 17:", get_record(table, 17))
print("missing 22:", get_record(table, 22))
put_record(table, 17, 8)
save_records(path, table)
restored = load_records(path, 5)
print("reloaded 17:", get_record(restored, 17))
Worked example
Theory extension: update a known fixed-size binary record
This self-created binary example explains direct byte access separately from Cambridge's text/ADT practical convention. Two initial records are (101,4) and (102,7). Slot 1 starts at byte 8; read exactly eight bytes, then verify the expected key before overwriting that record.
Increment quantity by two, seek back to byte 8 and write (102,9). Reading the same slot outputs (102, 9). Reading leaves the pointer after the record, so the second seek is necessary.
For classroom extension, reject out-of-range slots before seeking and report a short read. This example updates only a known valid existing record; it is not a complete persistent hash-file implementation.
Runnable Python 3 theory extension: direct byte accesspython
from pathlib import Path
from tempfile import TemporaryDirectory
import struct
record = struct.Struct(">II")
with TemporaryDirectory() as folder:
path = Path(folder) / "items.dat"
with path.open("wb") as target:
target.write(record.pack(101, 4))
target.write(record.pack(102, 7))
with path.open("r+b") as target:
target.seek(record.size)
data = target.read(record.size)
if len(data) != record.size:
raise ValueError("Incomplete record")
key, quantity = record.unpack(data)
if key != 102:
raise ValueError("Unexpected key")
target.seek(record.size)
target.write(record.pack(key, quantity + 2))
with path.open("rb") as source:
source.seek(record.size)
print(record.unpack(source.read(record.size)))
Original A-Level practice
6 original questions total 23 marks. Attempt each before opening the independently written indicative marking guidance.
Question 1
4 marks
A file contains two records. Explain the effects of READ, WRITE and APPEND, and distinguish a blank line from EOF.
Show solution and marking guidance+
Indicative answer
1 mark: READ obtains existing contents without intentionally replacing them.
1 mark: WRITE replaces existing contents/creates a fresh file.
1 mark: APPEND retains earlier contents and adds after them.
1 mark: a blank line is text data; EOF means no further record remains.
Question 2
4 marks
Five empty random-file slots numbered 0–4 use key MOD 5 and linear probing with wrap-around. Insert keys 12, 17 and 22 in that order, then explain why deleting 12 needs care.
Show solution and marking guidance+
Indicative answer
1 mark: 12 goes to slot 2.
1 mark: 17 collides and goes to slot 3.
1 mark: 22 probes 2,3 and goes to slot 4.
1 mark: a search for 17/22 must continue past deleted slot 2; use a tombstone or rebuild the collision chain.
Question 3
5 marks
A Paper 4 practice task supplies text records 12,4; 17,3; 9,5 and asks for simulated random access using five slots and key MOD 5 with linear probing. Explain loading, trace lookup 17, and explain how changing its quantity to 8 can survive a program restart.
Show solution and marking guidance+
Indicative answer
1 mark: read/parse the text records and insert them into an array-backed hash table using the explicit hash algorithm and collision policy.
1 mark: 17 hashes to slot 2, occupied by key 12.
1 mark: probe slot 3 and match key 17 to return (17,3).
1 mark: update that existing record to (17,8), rather than append an unreachable duplicate key.
1 mark: save records to text and rebuild the hash table on reloading; an unsaved in-memory update alone does not persist. Binary byte-seeking is unnecessary for this simulation.
Question 4
3 marks
An eight-byte record file uses zero-based slots. Give the byte address of slot 3 and explain why a second seek is needed before rewriting a just-read record.
Show solution and marking guidance+
Indicative answer
1 mark: slot 3 starts at byte 3×8=24.
1 mark: reading eight bytes advances the pointer to byte 32.
1 mark: seek back to 24 before replacing slot 3, otherwise the write affects the next position.
Question 5
3 marks
A programmer catches every exception and returns zero from total_quantity. Explain why this is unsafe and suggest a better approach.
Show solution and marking guidance+
Indicative answer
1 mark: zero is indistinguishable from a valid successful empty/zero-total file.
1 mark: genuine missing/corrupt files or programming defects are hidden.
1 mark: catch specific recoverable failures, report them clearly, and preserve failure rather than manufacture success.
Question 6
4 marks
Design four meaningful tests for the text-file function described below, stating expected results.
Function and record format to test
total_quantity(path) reads a UTF-8 file with one item-code,quantity record per line and adds every quantity to a total initialised to 0. Example input records are A,4 and B,3.
Each record must have exactly one comma, a non-empty item code and an integer quantity at least 0. Invalid records raise ValueError; a missing file raises FileNotFoundError. An APPEND operation can add another record while preserving earlier lines.
Show solution and marking guidance+
Indicative answer
1 mark: normal records A,4 and B,3 give total 7.
1 mark: empty file gives total 0 without an invalid read.
1 mark: non-numeric or negative quantity raises ValueError.
1 mark: missing file raises FileNotFoundError, or an append test confirms that previous records are retained and total increases appropriately.
Specification and references
This guide addresses Cambridge International 9618 9618 (2027–2029): 10.3, 13.2, 20.2; Paper 4 practical scope. Check your examination year and the complete specification for the assessment scope.
These are independently written explanations and practice questions. CompSciTutoring.co.uk is not affiliated with or endorsed by an examination board. The marking guidance is indicative; always check the syllabus for your examination year.