S5.0 What This Teaches
- Opening files with
open()and thewithstatement - Reading:
read(),readline(),readlines() - Writing:
write()andwritelines() - Working with paths using
pathlib.Path - Directory operations and file enumeration
- Reading and writing binary data
S5.1 Opening Files
# Always use with - file closes even if an exception occurs
with open("notes.txt", "w") as f:
f.write("Hello, file!\n")
f.write("Second line.\n")
# Read modes
# "r" - read text (default)
# "w" - write text (creates or truncates)
# "a" - append text
# "rb" - read binary
# "wb" - write binary
with open("notes.txt", "r") as f:
content = f.read() # read entire file as one string
print(content)
with block, a file handle is only closed when garbage
collected. On Windows this can prevent other code from opening the file.
Always use with.
S5.2 Reading Files
with open("notes.txt") as f:
# Read entire file
content = f.read()
with open("notes.txt") as f:
# Read one line at a time (memory-efficient)
line = f.readline() # includes trailing \n
while line:
print(line, end="")
line = f.readline()
with open("notes.txt") as f:
# Read all lines into a list
lines = f.readlines() # each line includes \n
# Best for large files: iterate the file object directly
with open("notes.txt") as f:
for line in f:
print(line.strip())
S5.3 Writing Files
# Write a string
with open("output.txt", "w") as f:
f.write("line one\n")
f.write("line two\n")
# Write multiple lines at once
lines = ["alpha\n", "beta\n", "gamma\n"]
with open("output.txt", "w") as f:
f.writelines(lines) # writelines does not add newlines
# Append to existing file
with open("output.txt", "a") as f:
f.write("appended\n")
# print() works with file=
with open("output.txt", "a") as f:
print("printed line", file=f) # adds newline automatically
S5.4 Working with Paths using pathlib
from pathlib import Path
p = Path("data/notes.txt")
print(p.name) # "notes.txt"
print(p.stem) # "notes"
print(p.suffix) # ".txt"
print(p.parent) # "data"
print(p.resolve()) # absolute path
# Build paths with /
config = Path.home() / ".config" / "app.json"
# Test existence
print(p.exists())
print(p.is_file())
print(p.is_dir())
# Convenience read/write (small files)
p.write_text("Hello from pathlib!")
text = p.read_text()
# Enumerate files
for f in Path(".").glob("*.txt"):
print(f.name)
S5.5 Directory Operations
import os
from pathlib import Path
# Create directories
Path("mydir/subdir").mkdir(parents=True, exist_ok=True)
# List directory
for item in Path(".").iterdir():
print(item.name, "dir" if item.is_dir() else "file")
# Recursive glob
for py_file in Path(".").rglob("*.py"):
print(py_file)
# Delete
import shutil
Path("file.txt").unlink(missing_ok=True) # delete file
shutil.rmtree("mydir") # delete directory tree
# os.path (older API, still common)
import os.path
print(os.path.join("a", "b", "c.txt"))
print(os.path.exists("notes.txt"))
S5.6 Binary Files
import struct
# Write binary: pack ints and floats
with open("data.bin", "wb") as f:
f.write(struct.pack("<if", 42, 3.14)) # little-endian int + float
# Read binary
with open("data.bin", "rb") as f:
raw = f.read(8)
i, x = struct.unpack("<if", raw)
print(i, x) # 42 3.14
# Simple bytes
with open("bytes.bin", "wb") as f:
f.write(bytes(range(10)))
with open("bytes.bin", "rb") as f:
data = f.read()
print(list(data)) # [0, 1, 2, ..., 9]
S5.7 Example - All Together
# File I/O - write CSV and read it back, computing average score.
from pathlib import Path
csv_path = Path("scores.csv")
records = [("Alice", 92), ("Bob", 78), ("Carol", 95)]
# Write CSV
with csv_path.open("w") as f:
f.write("Name,Score\n")
for name, score in records:
f.write(f"{name},{score}\n")
# Read and process
total = 0
count = 0
with csv_path.open() as f:
next(f) # skip header
for line in f:
_, score = line.strip().split(",")
total += int(score)
count += 1
print(f"Average score: {total / count:.1f}")
Average score: 88.3
S5.8 Exercise
Exercise
- Write a function that counts the number of lines, words, and characters
in a text file (like
wcon Unix). Test it on any file. - Use
pathlib.Path.rglob("*.py")to find all Python files in the current directory tree. Print each path relative to the current directory. - Write a binary file containing a sequence of integers using
struct.pack, then read it back and print the sum.
S5.9 Common Mistakes
Opening a file without with
f = open("data.txt")
content = f.read()
# f.close() forgotten - handle stays open until GC
# Use with open("data.txt") as f: instead
Opening a binary file in text mode
with open("image.png") as f: # text mode on binary data
data = f.read() # UnicodeDecodeError or corruption
with open("image.png", "rb") as f: # correct
data = f.read()
Using os.path when pathlib is available
pathlib.Path is the modern API. It's object-oriented, composable
with /, and avoids many string-concatenation errors.
Prefer it over os.path in new code.
S5.10 Key Terms
| Term | Meaning |
|---|---|
| open() | Built-in that returns a file object; mode controls read/write/binary |
| with | Ensures the file is closed after the block, even on exception |
| read() | Reads the entire file or up to n bytes/chars into a string or bytes |
| readline() | Reads one line including the trailing newline |
| write() | Writes a string or bytes; does not add a newline |
| pathlib.Path | Object-oriented path; supports /, glob(), read_text(), write_text() |
| glob / rglob | Pattern-matching file search; rglob is recursive |
| struct | Standard library module for packing/unpacking binary data |