Data lives in files: notes, spreadsheets exported as CSV, settings in JSON. Programs that read and write files can clean data, make reports and keep information between runs. In this lesson you learn the three formats you will meet most: plain text, CSV and JSON. The program below creates its own files in a temporary folder and removes them at the end, so you can run it safely.
The with statement
open(path) gives you a file object. When you finish, the file must be closed, or data can be lost. The safest way is with open(...) as f:. When the indented block ends, even if an error happens, Python closes the file for you. Always give encoding="utf-8" for text, so names and symbols in any language work the same on every computer. The modes are "r" to read (the default), "w" to write and replace everything, and "a" to add at the end.
pathlib: paths that work everywhere
The Path class from pathlib builds file paths in a way that works on Windows, macOS and Linux. folder / "notes.txt" joins parts of a path with the right separator. It also has handy methods: .exists(), .mkdir(), .read_text(), .write_text(), .iterdir() and .name. Prefer Path to typing slashes and backslashes by hand.
import csv
import json
from pathlib import Path
folder = Path("data_demo")
folder.mkdir(exist_ok=True)
# Text file
notes = folder / "notes.txt"
notes.write_text("first line\nsecond line\n", encoding="utf-8")
with open(notes, encoding="utf-8") as f:
for number, line in enumerate(f, start=1):
print(number, line.strip())
# Append
with open(notes, "a", encoding="utf-8") as f:
f.write("third line\n")
print(len(notes.read_text(encoding="utf-8").splitlines()), "lines now")
# CSV
rows = [
{"date": "2026-01-05", "category": "Food", "amount": 250},
{"date": "2026-01-06", "category": "Travel", "amount": 120},
]
csv_file = folder / "expenses.csv"
with open(csv_file, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["date", "category", "amount"])
writer.writeheader()
writer.writerows(rows)
with open(csv_file, newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
print(row["category"], int(row["amount"]))
# JSON
settings = {"currency": "INR", "budget": 5000, "tags": ["home", "work"]}
json_file = folder / "settings.json"
json_file.write_text(json.dumps(settings, indent=2), encoding="utf-8")
loaded = json.loads(json_file.read_text(encoding="utf-8"))
print(loaded["budget"], loaded["tags"])
print(sorted(p.name for p in folder.iterdir()))
print(Path("missing.txt").exists())
import shutil
shutil.rmtree(folder)
1 first line
2 second line
3 lines now
Food 250
Travel 120
5000 ['home', 'work']
['expenses.csv', 'notes.txt', 'settings.json']
False
Understanding the example
- Text:
write_textcreates the file. Looping over a file object withfor line in fgives one line at a time, which is good for big files, andline.strip()removes the line break at the end. - Append: mode
"a"added the third line. The program then counted 3 lines. - CSV: CSV means comma-separated values, the plain-text form of a spreadsheet.
csv.DictWriterwrites rows from dictionaries, andcsv.DictReaderreads each row as a dictionary with the column names as keys. Open CSV files withnewline="", as the documentation says. Everything read from a CSV is text, so convert numbers yourself, as inint(row["amount"]). - JSON:
json.dumpsturns Python data into JSON text,indent=2makes it readable, andjson.loadsturns it back into dictionaries and lists. JSON handles numbers, text, lists, dictionaries,true,falseandnull(Python'sNone). It cannot store everything, for example dates, which you must convert to text. - Cleaning up:
shutil.rmtree(folder)deletes a folder and everything inside it. Be careful with it: there is no recycle bin.
Read a whole file or line by line?
f.read()returns everything as one text. Fine for small files.for line in f:handles one line at a time and uses little memory. Use it for large files.f.readlines()gives a list of all lines.
Where does Python look for a file?
A relative path such as "notes.txt" is relative to the current folder of the terminal, not to the folder where your script is stored. If you run the script from another folder, the file may not be found. When your script needs to find a file that sits next to it, build the path from the script's location: Path(__file__).parent / "notes.txt". You will do this in lesson 13.
Safety with files
- Opening a file with
"w"erases the old content at once. Use"a"to add, or check withexists()first. - Do not delete or overwrite files from a program until you have tested it on copies.
- Never build file paths from text typed by an untrusted user without checking, because input like
..can climb out of your folder. - Keep secrets such as passwords out of files that you share or upload.
Your task
- Write a program that saves three lines of your own into
notes.txt, then reads and prints them with line numbers. - Create a CSV file
students.csvwith columnsnameandmarksand four rows. Read it, convert marks to numbers and print the average. - Save a dictionary of your settings as
settings.jsonand load it again. Print one value. - Print the names of the files in the current folder using
Path(".").iterdir().
How to check it worked
- The notes are printed in order with numbers 1 to 3.
- The average of your marks matches a calculation by hand.
- The loaded JSON value is the same as the one you saved.