Reading & Writing Files
Programs need to save data and read it back: settings, logs, reports, datasets. Python's built-in open() handles text and binary files, and the with statement makes sure files are always closed properly.
Writing a file
with open("notes.txt", "w", encoding="utf-8") as f: f.write("First line\n") f.write("Second line\n") print("Third line", file=f) print("saved")
saved
open(path, mode) returns a file object. Mode "w" creates the file, or overwrites it if it exists. write() doesn't add newlines for you; print(..., file=f) does. The with block closes the file automatically when it ends — even if an error occurs.
encoding="utf-8" for text files. Without it, Python uses the system's default encoding, which differs between Windows and other systems and can garble accented characters and emoji.Reading a file
with open("notes.txt", "w", encoding="utf-8") as f: f.write("apples\nbananas\ncherries\n") with open("notes.txt", encoding="utf-8") as f: content = f.read() print(repr(content)) with open("notes.txt", encoding="utf-8") as f: for line in f: print(line.rstrip("\n").upper()) with open("notes.txt", encoding="utf-8") as f: lines = f.read().splitlines() print(lines)
'apples\nbananas\ncherries\n' APPLES BANANAS CHERRIES ['apples', 'bananas', 'cherries']
f.read()returns the whole file as one string.- Looping over the file gives one line at a time, including its trailing
\n. This is memory-efficient even for huge files. f.read().splitlines()gives a list of lines without newline characters.
File modes
| Mode | Meaning |
|---|---|
"r" | Read (default). Error if the file doesn't exist |
"w" | Write. Creates or truncates the file |
"a" | Append to the end. Creates the file if needed |
"x" | Create; error if the file already exists — safe against overwriting |
"b" | Binary, combined with the above: "rb", "wb" |
"+" | Read and write: "r+" |
Appending
from datetime import datetime def log(message): stamp = datetime(2026, 10, 6, 9, 30).strftime("%H:%M") with open("app.log", "a", encoding="utf-8") as f: f.write(f"{stamp} {message}\n") open("app.log", "w").close() log("started") log("processed 42 rows") with open("app.log", encoding="utf-8") as f: print(f.read(), end="")
09:30 started 09:30 processed 42 rows
Handling missing files
def read_settings(path): try: with open(path, encoding="utf-8") as f: return f.read() except FileNotFoundError: print(f"{path} not found, using defaults") return "" read_settings("does-not-exist.ini")
does-not-exist.ini not found, using defaults
Processing a file line by line
data = """name,score Ana,91 Ben,78 Cleo,85 """ with open("scores.csv", "w", encoding="utf-8") as f: f.write(data) total = count = 0 with open("scores.csv", encoding="utf-8") as f: next(f) for line in f: name, score = line.strip().split(",") total += int(score) count += 1 print(f"{count} students, average {total / count:.1f}")
3 students, average 84.7
next(f) skips the header row. For real CSV files with quoted commas, use the csv module (lesson 45).
Binary files
data = bytes([0x89, 0x50, 0x4E, 0x47]) with open("header.bin", "wb") as f: f.write(data) with open("header.bin", "rb") as f: raw = f.read() print(raw, raw[1:].decode("ascii"), len(raw))
b'\x89PNG' PNG 4
Binary mode reads and writes bytes instead of str — use it for images, PDFs, archives and any non-text format. Never pass encoding in binary mode.
Several files at once
with open("notes.txt", "w", encoding="utf-8") as f: f.write("b\na\nc\n") with open("notes.txt", encoding="utf-8") as src, open("sorted.txt", "w", encoding="utf-8") as dst: dst.writelines(sorted(src)) with open("sorted.txt", encoding="utf-8") as f: print(f.read().split())
['a', 'b', 'c']
Try it yourself
The editor runs Python in your browser with a temporary, in-memory file system, so you can create and read files freely: