Python · Lesson 11 of 15
File Handling
Read and write text files safely with context managers.
- Intermediate
- 12 min read
- 3 objectives
Before this lessonLesson 10: Inheritance and Special Methods
What you will learn
- Use with open()
- Read and write text
- Handle missing files
Real programs read configuration, process logs and save results, all of which means working with files. Python's open() gives you a file object, and the safest way to use it is inside a with block. The block guarantees the file is closed when you leave it, even if an error happens.
Why programs need files
Variables disappear the moment your program ends. A file is how information survives: settings, saved games, reports, logs. Reading and writing files lets your program remember things between runs, and lets it work with data created by other people and tools.
Think of a file as a notebook. To use it you open it, then read or write, then close it. Python's with statement does the closing for you, even when something goes wrong.
Writing a file
The second argument is the mode: "w" writes and erases any existing content, "a" appends, "r" reads (the default). Always pass encoding="utf-8" so text behaves the same on every operating system.
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("first line\n")
f.write("second line\n")Reading a file
f.read() returns the whole file as one string, f.readlines() returns a list of lines, and looping over the file object reads one line at a time, which keeps memory use low for large files.
with open("notes.txt", encoding="utf-8") as f:
for line in f:
print(line.strip())first line second line
strip() removes the trailing newline character that every line carries.
Appending and paths
Use mode "a" to add to the end of a file without erasing it. For building paths that work on Windows, macOS and Linux, prefer pathlib over string concatenation.
from pathlib import Path
path = Path("data") / "log.txt"
path.parent.mkdir(exist_ok=True)
with path.open("a", encoding="utf-8") as f:
f.write("started\n")
print(path.exists(), path.read_text(encoding="utf-8"))Handling missing files
Opening a file that does not exist raises FileNotFoundError. Catch that specific exception to give a helpful fallback.
try:
with open("missing.txt", encoding="utf-8") as f:
data = f.read()
except FileNotFoundError:
data = ""
print("no file, using empty data")no file, using empty data
The three modes you need
"r"read (the default): fails if the file does not exist."w"write: creates the file, erasing anything already there."a"append: adds to the end without erasing.
Write, then read back
with open("notes.txt", "w") as f:
f.write("first line\n")
f.write("second line\n")
with open("notes.txt", "a") as f:
f.write("third line\n")
with open("notes.txt") as f:
print(f.read())first line second line third line
Reading line by line
For big files do not load everything at once. Looping over the file gives you one line at a time using very little memory. Each line ends with \n, so call .strip() to remove it.
with open("notes.txt") as f:
for number, line in enumerate(f, start=1):
print(number, line.strip())1 first line 2 second line 3 third line
Worked example: total a CSV of expenses
with open("expenses.csv", "w") as f:
f.write("item,amount\ncoffee,3.5\nbooks,20\ntrain,12.25\n")
total = 0
with open("expenses.csv") as f:
next(f) # skip the header row
for line in f:
item, amount = line.strip().split(",")
total += float(amount)
print(f"Total: {total:.2f}")Total: 35.75
Handling a missing file
try:
with open("does-not-exist.txt") as f:
data = f.read()
except FileNotFoundError:
data = ""
print("No file yet, starting empty")
print(repr(data))No file yet, starting empty ''
Key takeaways
- Open files with
with open(path, mode) as fso they always close. rreads,woverwrites,aappends.- Loop over a file to read it line by line;
.strip()removes the newline. - Catch
FileNotFoundErrorwhen a file might not exist yet.
# Write your solution here
