Reading and Writing Files
Variables disappear when a program ends. To keep data, such as notes, scores, settings or logs, you save it to a file. Python makes reading and writing files straightforward.
About this page: the code here runs in your browser, so files are saved in a temporary in-browser file system. They're kept while this page is open, so one example can read a file another example wrote, and they disappear when you reload the page. On your own computer, the same code reads and writes real files.
Writing a file
open(filename, mode) opens a file. The mode "w" means write: it creates the file, or replaces it if it already exists. The with statement closes the file automatically when the block ends:
with open("notes.txt", "w") as f:
f.write("Buy milk\n")
f.write("Call Ada\n")
f.write("Learn Python\n")
print("Saved!")write() doesn't add a newline for you, so add \n yourself at the end of each line.
Reading a whole file
Mode "r" (read) is the default, so you can leave it out:
with open("notes.txt") as f:
content = f.read()
print(content)
print(type(content), len(content), "characters")Reading line by line
Looping over a file gives you one line at a time. That's memory-friendly even for huge files:
with open("notes.txt") as f:
for number, line in enumerate(f, start=1):
print(number, line.strip())Each line still ends with \n, which is why we use strip(). Use readlines() to get all the lines as a list:
with open("notes.txt") as f:
lines = f.readlines()
print(lines)
print([line.strip() for line in lines])Appending
Mode "a" (append) adds to the end of a file instead of replacing it:
with open("notes.txt", "a") as f:
f.write("Go for a run\n")
with open("notes.txt") as f:
print(f.read())File modes
| Mode | Meaning | If the file exists | If it doesn't |
|---|---|---|---|
"r" |
Read (default) | reads it | FileNotFoundError |
"w" |
Write | erases it first | creates it |
"a" |
Append | adds to the end | creates it |
"x" |
Create | FileExistsError |
creates it |
Add b for binary files such as images ("rb", "wb").
Careful with "w": opening an existing file in write mode erases its contents immediately, even if you never write anything.
Why use with?
Without with, you must remember to call close() yourself, and if an error happens first, the file stays open:
f = open("notes.txt")
print(f.readline().strip())
f.close()with guarantees the file is closed, even if an exception happens inside the block. Always prefer it.
Going deeper: Accents and emoji: set the encoding optional
A file stores bytes, not letters. The encoding decides how letters such as é, ñ or ☕ become bytes. If you don't choose one, Python uses your computer's default, which on Windows is often not UTF-8. A file with accents written on one machine can then come out garbled, or raise a UnicodeDecodeError, on another.
Passing encoding="utf-8" when you open the file avoids all of that:
with open("menu.txt", "w", encoding="utf-8") as f:
f.write("Café crème ☕\n")
with open("menu.txt", encoding="utf-8") as f:
print(f.read())UTF-8 can store every character in every language, and it's what the web uses. Make encoding="utf-8" a habit for every text file.
Going deeper: Working with paths using pathlib optional
The pathlib module treats a file path as an object with handy methods, instead of a plain string:
from pathlib import Path
path = Path("notes.txt")
path.write_text("Buy milk\nCall Ada\n", encoding="utf-8")
print(path.exists())
print(path.name, path.stem, path.suffix)
print(path.read_text(encoding="utf-8"))/ joins path pieces and uses the right separator for Windows, macOS or Linux:
from pathlib import Path
folder = Path("reports")
folder.mkdir(exist_ok=True)
report = folder / "2026" / "summary.txt"
print(report)
print(report.parent)write_text() and read_text() are great for small files. For line-by-line reading, path.open() works just like open().
Handling a missing file
Reading a file that doesn't exist raises FileNotFoundError. Catch it like any other exception:
try:
with open("missing.txt") as f:
print(f.read())
except FileNotFoundError:
print("The file doesn't exist yet.")The os module can check first, and list or delete files:
import os
print(os.path.exists("notes.txt"))
print(os.path.exists("missing.txt"))
print(os.listdir("."))Writing and reading numbers
Files hold text, so convert numbers to strings when writing and back again when reading:
scores = [88, 92, 75, 64]
with open("scores.txt", "w") as f:
for score in scores:
f.write(f"{score}\n")
with open("scores.txt") as f:
loaded = [int(line) for line in f]
print(loaded, "average:", sum(loaded) / len(loaded))CSV files
CSV (comma-separated values) is the simplest spreadsheet format. The csv module handles the details, such as commas inside quoted values:
import csv
rows = [
["name", "city", "score"],
["Ada", "London", 92],
["Grace", "New York", 88],
]
with open("people.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(rows)
with open("people.csv") as f:
for row in csv.DictReader(f):
print(f"{row['name']} from {row['city']} scored {row['score']}")JSON files
JSON is the standard format for structured data on the web, and it maps directly onto Python dictionaries and lists:
import json
settings = {"theme": "dark", "font_size": 14, "recent": ["main.py", "notes.txt"]}
with open("settings.json", "w") as f:
json.dump(settings, f, indent=2)
with open("settings.json") as f:
print(f.read())
with open("settings.json") as f:
loaded = json.load(f)
print(loaded["recent"][0])Exercises
Exercise 1: Write and read
Write the three lines red, green and blue to colors.txt, then read the file back and print each colour in upper case.
# write colors.txt, then read it back
Open the file with "w" and write each colour followed by \n.
Open it again (read mode is the default), loop over the lines and print line.strip().upper().
Exercise 2: Count lines and words
Create story.txt with the given text, then print how many lines and how many words it has. It should print Lines: 3 and Words: 15.
text = "Once upon a time\nthere was a small python\nwho loved to code all day\n"
Write text to the file, then reopen it and loop over its lines.
Each pass of the loop is one line, and len(line.split()) gives the words on that line.
Exercise 3: Visit log
Write a function log_visit(name) that appends a line Visited by <name> to log.txt. Call it three times, then print the file.
def log_visit(name):
pass
Open the file in append mode, "a", so each call adds a line instead of replacing the file.
Write f"Visited by {name}\n", then read the whole file back with read() and print it.
Exercise 4: Save a high score
Save the dictionary to scores.json, load it back, and print the name with the highest score: Leo.
import json
high_scores = {"Mia": 1200, "Leo": 1850, "Ava": 990}
json.dump(data, f) saves the dictionary, and json.load(f) loads it back.
max(loaded, key=loaded.get) gives the key with the biggest value.
File tools at a glance
A quick reference for later. f is a file opened with with open(...) as f:.
| Code | What it does |
|---|---|
open(name, "w", encoding="utf-8") |
Opens a file for writing (replaces it). Use "a" to append and "r" (the default) to read |
f.write(text) |
Writes a string; add \n yourself for new lines |
f.writelines(lines) |
Writes every string in a list (again, no new lines added) |
f.read() |
Reads the whole file into one string |
f.readline() |
Reads the next line, including its \n |
f.readlines() |
Reads all lines into a list |
for line in f: |
Goes through the file one line at a time (memory-friendly) |
line.strip() |
Removes the \n (and spaces) from the ends of a line |
os.path.exists(name) |
Does the file exist? (import os first) |
Path(name).read_text() / .write_text(text) |
Reads or writes a whole file in one step (from pathlib import Path) |
json.dump(data, f, indent=2) |
Saves lists and dictionaries to a JSON file |
json.load(f) |
Loads a JSON file back into lists and dictionaries |
json.dumps(data) / json.loads(text) |
The same, but to and from a string instead of a file |
csv.writer(f).writerow(row) |
Writes one row to a CSV file; writerows(rows) writes many |
csv.reader(f) |
Reads a CSV file as lists of strings, one per row |
csv.DictReader(f) |
Reads a CSV file as dictionaries, using the header row as keys |
Summary
open(name, mode)opens a file:"r"reads,"w"writes (and erases),"a"appends.with open(...) as f:closes the file automatically. Always use it.f.read()reads everything, andfor line in f:reads line by line. Usestrip()to remove the\n.f.write()writes text. Add\nyourself, and convert numbers withstr()or f-strings.- A missing file raises
FileNotFoundError, andos.path.exists()checks first. - Use the
csvmodule for spreadsheets andjsonfor structured data.