File I/O

This is a bridge note. The PDF anchor for file input/output is stored under programming_fundamentals, but the official syllabus point on storing and retrieving data from serial and sequential text files belongs to 2.3 Implementing Algorithms and Data Structures.

File I/O matters because variables disappear when a program ends, but files persist on disk.

Caption: This is a read-transform-write workflow, not a rule that every task must both read and write. Choose the mode first, perform the required read and/or write operations, and let with open(...) close the file when the context exits.

Why Use Files?

Files allow a program to:

  • read a large amount of data without manual re-entry;
  • store output for later use;
  • exchange data with other programs;
  • keep records between program runs.

A file is a named location on storage. For syllabus work, focus first on text files because their contents can be read as characters and lines.

Opening Files

Python uses open() to open a file.

file = open("scores.txt", "r")
contents = file.read()
file.close()

The mode controls the operation:

ModeMeaningImportant risk
"r"readfile must exist
"w"writecreates a missing file; truncates existing contents
"a"appendcreates a missing file; otherwise adds to the end

Prefer the with form because it closes the file automatically:

with open("scores.txt", "r", encoding="utf-8") as file:
    contents = file.read()

Reading Methods

read() reads the whole file as one string.

with open("message.txt", "r", encoding="utf-8") as file:
    text = file.read()

readline() reads one line at a time.

It returns "" at end-of-file. When a stored line has a newline, the returned string normally retains "\n"; use strip() only when removing surrounding whitespace is intended.

with open("message.txt", "r", encoding="utf-8") as file:
    first_line = file.readline()

readlines() reads all lines into a list of strings.

with open("message.txt", "r", encoding="utf-8") as file:
    lines = file.readlines()

For many exam-style tasks, iterating directly over the file is simple and memory-friendly:

with open("scores.txt", "r", encoding="utf-8") as file:
    for line in file:
        score = int(line.strip())
        print(score)

Writing and Appending

Writing:

Caption: Mode "a" creates a missing file or appends after existing contents; mode "w" creates a missing file or truncates an existing one before writing. write() adds no newline automatically.

with open("result.txt", "w", encoding="utf-8") as file:
    file.write("Pass\n")

Appending:

with open("log.txt", "a", encoding="utf-8") as file:
    file.write("Program started\n")

Remember that write() does not automatically add a newline. Add "\n" when each item should appear on a new line.

For beginners, it helps to separate two kinds of state:

StateWhere it livesExample
variable statememory while the program runspasses = 3
file statestorage that persists after the program endsthe lines inside scores.txt

Reading copies text from the file into variables. Writing or appending changes the file contents.

Serial and Sequential Text Files

These terms describe file organisation, not Python access modes.

A serial file stores records in the order they were added. To find a record, the program may need to read from the start until it reaches the target.

A sequential file stores records in order by a key field. The key field is unique and ordered, but not necessarily consecutive.

For example:

1001,Aisha,72
1004,Bo,85
1008,Chen,68

If the first field is the key and records are sorted by ascending unique key, search begins at the start and may stop unsuccessfully once the current key exceeds the target. Otherwise it stops when found or at end-of-file.

Processing Line-Based Data

Example: read marks from lines and count passes.

def count_passes(path):
    passes = 0
    with open(path, "r", encoding="utf-8") as file:
        for line in file:
            mark = int(line.strip())
            if mark >= 50:
                passes = passes + 1
    return passes

The important state change is passes = passes + 1, which updates the count only when the current mark is at least 50.

Contract for this simple version: every line is non-blank and contains one integer mark in 0..100; an empty file is allowed and returns 0. A blank or malformed line violates the precondition and the unguarded example raises ValueError. If validity is not guaranteed, implement a clear reject or skip-and-report policy. Test empty, one-line, marks 0/50/100, blank, and malformed files.

Worked debugging method: under a reject policy, a blank line should produce a clear line-numbered error, whereas direct int(line.strip()) gives an unexplained ValueError. Trace line number and cleaned text, validate before conversion, then rerun empty, valid, blank, and malformed inputs.

Updating Records in a Text File

Text files are normally processed sequentially. To change or remove a record safely, a common approach is:

  1. read the original file;
  2. write the required records to a new temporary file;
  3. replace the old file after processing succeeds.

Do not expect a simple text file to support changing the length of one middle line in place.

CSV-Style Data

The csv module is useful practical support rather than a separately named core operation. Define behaviour for empty, header-only, short, and malformed rows; next(reader) on an empty file raises StopIteration unless guarded.

CSV stands for comma-separated values. It stores tabular data as text.

name,mark
Aisha,72
Bo,85
Chen,68

Python has a csv module for robust CSV processing. For simple syllabus examples, first understand the idea of one record per line and fields separated by commas.

Precondition for the concise snippet below: the file exists, has a header, and every later row contains at least two fields with an integer mark in field 1. Empty/header-only/short/malformed inputs require validation before next(reader), indexing, or conversion.

import csv
 
with open("marks.csv", "r", encoding="utf-8", newline="") as file:
    reader = csv.reader(file)
    header = next(reader)
    for row in reader:
        name = row[0]
        mark = int(row[1])
        print(name, mark)

JSON Resource Files

This is optional enrichment. It helps connect file processing to practical resource files, but JSON processing is outside the core syllabus outcome on serial and sequential text files unless a task explicitly supplies this context.

Some practical tasks provide JSON files instead of plain text or CSV. JSON stores structured data using familiar shapes:

JSON shapePython shape after loading
objectdictionary
arraylist
stringstring
numberinteger or float
true/falseTrue/False

Example JSON file:

[
  {"asset_id": "L001", "kind": "laptop", "age_days": 380},
  {"asset_id": "T014", "kind": "tablet", "age_days": 1250}
]

Read it with Python’s json module:

import json
 
with open("devices.json", "r", encoding="utf-8") as file:
    devices = json.load(file)
 
for device in devices:
    if device["age_days"] > 1000:
        print(device["asset_id"], device["kind"])

The important skill is inspecting the loaded structure before writing the full solution. If you are unsure what shape the data has, print a small sample:

print(type(devices))
print(devices[0])

Do not guess field names. Read them from the supplied file or from the question.

Date and Time Fields in Files

This is optional enrichment. It is useful for practical tasks that supply date fields, but detailed date parsing is not an examinable core requirement of file I/O unless the question provides the required format or library context.

Resource files may store dates as strings. Before calculating with dates, convert the string into a date or datetime object.

from datetime import datetime
 
raw_date = "2026-07-08"
installed_on = datetime.strptime(raw_date, "%Y-%m-%d").date()
today = datetime.strptime("2026-07-20", "%Y-%m-%d").date()
 
age_days = (today - installed_on).days
print(age_days)

The format string must match the file data:

File valueFormat string
2026-07-08"%Y-%m-%d"
08/07/2026"%d/%m/%Y"
2026-07-08 14:30"%Y-%m-%d %H:%M"

If date conversion fails, check the exact separator, field order, and whether there are leading or trailing spaces.

Common Bugs

  • Opening a file from the wrong folder.
  • Using "w" when you meant "a", causing old contents to be overwritten.
  • Forgetting to close a file when not using with.
  • Forgetting that file data is read as text and may need int() or float().
  • Guessing JSON keys without inspecting the loaded dictionary.
  • Treating date strings as dates before converting them.
  • Forgetting to strip newline characters before conversion or comparison.
  • Writing values without converting them to strings.

Check Your Understanding

Try these before looking back at the explanations:

  1. Which mode overwrites an existing file?
  2. Which mode adds to the end of an existing file?
  3. Why is line.strip() often used before int(line.strip())?
  4. Why is with open(...) as file: preferred over manually calling close()?
  5. After json.load(file), what Python types should you expect?
  6. Why must a date string be converted before calculating the number of days between two dates?

Answers:

  1. "w".
  2. "a".
  3. It removes newline characters and surrounding whitespace before conversion.
  4. Python closes the file automatically when the with block ends.
  5. Usually dictionaries, lists, strings, numbers, and Boolean values, depending on the JSON structure.
  6. String subtraction has no date meaning; Python needs date or datetime objects to calculate a duration.