Grade by Grade
A. Programming Concepts and Skills · Data Types, Files and Expressions

Reading and Writing Text and CSV Files

A3.1 A1.2 A1.5 A4.1About 75 minutes
Learning goals
  • I can open a text file for reading, writing or appending with with and an explicit encoding.
  • I can read a file line by line, split each line into fields and convert them safely.
  • I can read and write CSV files with the csv module and load them into a list of records.
  • I can handle missing files and bad lines gracefully, using the error messages to find the cause.

Words to know

file
Named data stored on a disk (or other storage) that lasts after the program ends.
text file
A file of characters, organized in lines ending with a newline; readable in any text editor.
file mode
How a file is opened: "r" read, "w" write (erases first), "a" append, "x" create new only; add "b" for binary.
`with` statement
Opens a resource and guarantees it is closed at the end of the block, even if an error occurs.
CSV
Comma-separated values: a text format with one record per line and fields separated by commas.
encoding
The rule for turning characters into bytes, such as UTF-8. Always state it so accented letters and symbols survive.
current working directory
The folder a program uses for relative paths such as "snow.csv".

Data that outlives the program

Everything you have stored so far disappears when the program ends. Real programs keep data in files: a game saves your progress, a school system stores marks, a weather station logs readings every minute. The curriculum expects you to read from and write to external files of several kinds. This lesson covers the most common: text files and CSV files. Lessons 12 and 13 add binary files, JSON, XML and databases.

Note: code in practice questions is shown one line per bullet. Type it yourself rather than copying it, so the spaces and quotes are correct.

your program(memory)snow.csv(disk)"r" read"w" write (erases first!)"a" append to the endthe file outlives the program; memory does notalways open with encoding="utf-8"
Reading moves data from a file on disk into the program's memory; writing moves it the other way. Mode "w" erases an existing file before writing; "a" adds to the end; "x" refuses to overwrite.

Opening, reading and closing

The pattern you should always use:

with open("snow.csv", encoding="utf-8") as f:

  • open returns a file object f.
  • The default mode is "r" (read).
  • encoding="utf-8" makes accented letters, ° and other symbols work the same on every computer. Without it, Python uses the system default, which differs between Windows, macOS and Linux.
  • When the with block ends, the file is closed automatically, even if an exception happens.

Ways to read: f.read() (the whole file as one string), f.readline() (one line), f.readlines() (a list of lines), or the best for big files, for line in f: (one line at a time, using little memory).

date,snow_cm\n2026-12-01,4.5\nfor line in f: gives "date,snow_cm\n" then "2026-12-01,4.5\n"line.strip().split(",") gives ["2026-12-01", "4.5"]still text: convert "4.5" with float()
A text file is one long sequence of characters. Newline characters (\n, yellow) end each line, and commas (green) separate fields in a CSV file. Python hands you one line at a time as a string.
total = 0.0
bad = 0
with open("snow.csv", encoding="utf-8") as f:
header = f.readline() # skip the header line
for line in f:
date, value = line.strip().split(",")
try:
total += float(value)
except ValueError:
bad += 1
print("Skipping bad value on", date)
print(f"Total snowfall: {total:.1f} cm ({bad} bad line)")

Reading a CSV file line by line. with closes the file automatically, even if an error happens. Every line arrives as a string ending in \n, so strip() it, split it, and convert with protection (Lesson 3). The sample file snow.csv is shown in the activity. Output, line by line: Skipping bad value on 2026-12-04 | Total snowfall: 24.0 cm (1 bad line)

Writing and appending

  • open(path, "w", encoding="utf-8") creates the file, or erases it if it already exists. There is no undo.
  • "a" appends to the end of an existing file (or creates it).
  • "x" creates a new file but raises FileExistsError if one is already there: a safe choice when overwriting would be a disaster.
  • f.write(text) writes exactly the text you give it. Add \n yourself. print(..., file=f) adds the newline for you.
  • Numbers must be converted to text (str or an f-string) before writing. Everything in a text file is characters.
days = [("Mon", -12), ("Tue", -8), ("Wed", -15)]
with open("report.txt", "w", encoding="utf-8") as f:
f.write("Daily lows\n")
for day, low in days:
f.write(f"{day}: {low} °C\n")
print("Coldest:", min(days, key=lambda d: d[1])[0], file=f)
with open("report.txt", "a", encoding="utf-8") as f:
f.write("End of report\n")
with open("report.txt", encoding="utf-8") as f:
print(f.read(), end="")

Writing and appending. write does not add a newline for you; print(..., file=f) does. The ° symbol is safe because the file is written and read as UTF-8. Output, line by line: Daily lows | Mon: -12 °C | Tue: -8 °C | Wed: -15 °C | Coldest: Wed | End of report

The csv module

Splitting on commas breaks as soon as a field contains a comma, such as "Smith, Jordan". The csv module follows the CSV rules (quotes around fields with commas, doubled quotes inside quotes) so you do not have to.

  • csv.reader(f) gives each row as a list of strings.
  • csv.DictReader(f) uses the header row as keys and gives each row as a dictionary.
  • csv.writer(f).writerow(list) writes one row, adding quotes where needed.
  • Open CSV files with newline="". The csv module writes \r\n line endings by default (the standard CSV ending); reading the file back in text mode turns them into ordinary \n newlines.
import csv
with open("snow.csv", newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
print(rows[0])
good = [r for r in rows if r["snow_cm"].replace(".", "", 1).isdigit()]
print(len(rows), len(good))
with open("big_days.csv", "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["date", "snow_cm"])
for r in good:
if float(r["snow_cm"]) >= 5:
w.writerow([r["date"], r["snow_cm"]])
with open("big_days.csv", encoding="utf-8") as f:
print(f.read(), end="")

The csv module handles quoting and commas inside fields for you. DictReader turns each row into a dictionary keyed by the header, a list of records (Lesson 7). Open CSV files with newline="" as the documentation recommends. Output, line by line: {'date': '2026-12-01', 'snow_cm': '4.5'} | 5 4 | date,snow_cm | 2026-12-03,12.0 | 2026-12-05,7.5

Worked example

Example 1: Load a file into a list of records

A file skaters.txt holds one skater per line in the form name;age;best_time_s, for example Maya;16;41.82. Write a function load(path) that returns a list of Skater dataclass records, skips blank lines, and reports (but skips) badly formatted lines with their line number.

  1. Define the record: @dataclass class Skater: name: str; age: int; best: float (each field on its own line).
  2. Open with with open(path, encoding="utf-8") as f: and loop with for n, line in enumerate(f, start=1): so you know each line number.
  3. line = line.strip(); if not line: continue skips blank lines.
  4. Inside try:, split with name, age, best = line.split(";") and append Skater(name, int(age), float(best)). A wrong number of fields raises ValueError during unpacking, and so do bad numbers.
  5. except ValueError: print(f"Line {n} skipped: {line!r}"). After the loop, return skaters.
  6. Separating loading from processing means the rest of the program works with clean records and never sees raw text.

A load function that returns clean records and reports each bad line by number. (The full program is the first question in Your turn.)

Read the error, then read the documentation (A4.1)

File errors name the problem exactly: FileNotFoundError, PermissionError, UnicodeDecodeError (wrong encoding) or ValueError (bad data in a line). Search the error name in the official Python documentation (docs.python.org, Built-in Exceptions and the open() entry in Built-in Functions) or run help(open) to see every mode and option.

Worked example

Example 2: When the file is not there

A program crashes with FileNotFoundError: [Errno 2] No such file or directory: 'snow.csv', even though snow.csv is in the project folder. Find the cause and make the program handle it gracefully.

  1. A relative path such as "snow.csv" is looked up in the current working directory, which is where the program was started from, not necessarily where the .py file is. Print os.getcwd() to see it.
  2. Fix the path: run the program from the project folder, or build the path from the script's own location: Path(__file__).parent / "snow.csv" (with from pathlib import Path).
  3. Handle the error for users: wrap the open in try: / except FileNotFoundError: and print a helpful message such as "Could not find snow.csv. Put it next to the program."
  4. PermissionError (no rights to the file) and IsADirectoryError are other common file errors. Catch OSError to cover all of them.

The working directory was not the project folder. Use a path relative to the script and catch FileNotFoundError with a clear message.

Just for fun

A text file walks into a café and orders a coffee.

The barista asks, "Why so empty today?" The file sighs: "Someone opened me in write mode."

Hands-on activity

Your own weather log

You need: Python, a thermometer or a weather app, and a few minutes each day for a week (or use the sample file below).

Step 1. Create snow.csv in your project folder with a text editor, exactly as shown (one line per bullet):

  • date,snow_cm
  • 2026-12-01,4.5
  • 2026-12-02,0
  • 2026-12-03,12.0
  • 2026-12-04,oops
  • 2026-12-05,7.5

Step 2. Run the programs in this lesson on it and check that you get the outputs shown.

Step 3. Write your own program that appends one line to weather_log.csv each time it runs, with today's date (datetime.date.today().isoformat()), the temperature and a note. Run it once a day for a week (or several times with made-up values).

Step 4. Write a second program that reads the log and prints the warmest day, the coldest day and the average, skipping any bad lines.

What to look for: with and encoding="utf-8" every time; "a" for the logger (not "w", which would erase the week's data); and bad lines reported, not crashing the program.

Your turn

1

Write the full load(path) function from Example 1.

2

Which mode would you use to add new high scores to the end of scores.txt without losing old ones?

3

What is in out.txt after this code? with open("out.txt", "w") as f: then f.write("A") and f.write("B")

4

Why should you strip() each line before splitting it into fields?

5

What exception is raised when opening a file that does not exist for reading?

6

Why use csv.reader instead of line.split(",") for a file with the field "Smith, Jordan"?

7

What is the difference between modes "w" and "x"?

More practice: warm-up

1

What keyword makes sure a file is closed automatically?

2

What is the default mode of open?

3

What encoding should you specify?

4

What type is every line read from a text file?

5

Which print argument sends output to a file?

More practice: on your own

1

Write code that counts the lines, words and characters in poem.txt (like the Unix wc command).

2

A file has one integer per line. Write code that writes only the even numbers to even.txt, one per line.

3

A program opens a file with open("data.csv") and never closes it, then another part of the program opens the same file for writing. What can go wrong?

4

Explain the error UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9. What is a likely cause and fix?

5

Write code that reads snow.csv with csv.reader and builds a list of (date, float) tuples, skipping the header and bad values.

Challenge

1

Write a function safe_save(path, lines) that never leaves a half-written file if the program crashes part-way through. Hint: write somewhere else first.

2

A log file is 20 GB. Why is for line in f: fine but f.read() or f.readlines() likely to fail?

3

Design a CSV format for a library's loans (book, borrower, due date) and explain three validation checks your loader should do.

Check your understanding

Quiz

1.

Which mode erases an existing file before writing?

2.

Why use with open(...) as f:?

3.

What does each iteration of for line in f: give?

4.

A field in a CSV file is "Toronto, ON". What is the safest way to read the file?

5.

Your program raises FileNotFoundError for a file that exists in the script's folder. What is the most likely cause?

6.

Which line writes the number 42 and a newline to an open text file f?

For parents and teachers

Big idea: A3.1 asks students to read from and write to external files, starting with text files. Converting fields (A1.2), building lists of records (A1.5) and using error messages and documentation to fix problems (A4.1) all come together here.

How to help: the week-long weather log is a good shared project. Check that your teen's logger uses append mode: if a day's data disappears, ask what mode they used.

Common difficulties:

  • Using "w" when they meant "a" and losing data.
  • Forgetting that every line is a string ending in \n.
  • Omitting encoding="utf-8", so accented names break on another computer.
  • Running the program from a different folder and getting FileNotFoundError.