Python File Handling: The Complete Beginner's Guide
This chapter teaches persistent data workflows: safe file reads/writes, modes, CSV and JSON, robust error handling, binary operations, and modern path work using pathlib.
Chapter 14 of 20 · Intermediate · 45 min · Python Programming Course
Every program you have written so far forgets everything when it stops. File handling gives your programs permanent memory.
What You Will Learn in This Chapter
By the end of this tutorial you will be able to:
- Open files safely with
with open() - Read files using
read(),readline(),readlines(), and iteration - Write and append content correctly
- Understand file modes and their risks
- Work with CSV and JSON files
- Handle paths safely with
pathlib - Handle file errors robustly
- Work with binary files
- Avoid common file handling mistakes
Estimated time: 45 minutes reading + 25 minutes practice
The open() Function
file = open("example.txt", "r")
content = file.read()
file.close()
This works, but can leak resources if an exception occurs before close().
Always Use with
with open("example.txt", "r") as file:
content = file.read()
The file closes automatically, even on errors.
File Modes
| Mode | Name | Behavior |
|---|---|---|
| `r` | Read | File must exist |
| `w` | Write | Truncates existing file |
| `a` | Append | Writes at end, preserves content |
| `x` | Exclusive create | Fails if file exists |
| `b` | Binary | Use with other modes (`rb`, `wb`) |
| `+` | Read+Write | Combined read/write modes |
Reading Files
read()
with open("poem.txt", "r") as file:
content = file.read()
readline()
with open("data.txt", "r") as file:
first_line = file.readline()
readlines()
with open("students.txt", "r") as file:
lines = file.readlines()
Iteration (recommended default)
with open("students.txt", "r") as file:
for line in file:
print(line.strip())
Writing and Appending
with open("output.txt", "w") as file:
file.write("First line\n")
file.write("Second line\n")
with open("log.txt", "a") as file:
file.write("New log entry\n")
Working With CSV
import csv
with open("students.csv", "r", newline="") as file:
reader = csv.DictReader(file)
for row in reader:
print(row["name"], row["score"])
import csv
rows = [{"name": "Alice", "score": 88}, {"name": "Bob", "score": 75}]
with open("output.csv", "w", newline="") as file:
writer = csv.DictWriter(file, fieldnames=["name", "score"])
writer.writeheader()
writer.writerows(rows)
Working With JSON
import json
data = {"course": "Python", "students": [{"name": "Alice", "score": 88}]}
with open("course_data.json", "w") as file:
json.dump(data, file, indent=2)
with open("course_data.json", "r") as file:
loaded = json.load(file)
pathlib - Modern Path Handling
from pathlib import Path
path = Path("data") / "processed" / "output.csv"
print(path.exists())
from pathlib import Path
p = Path("notes.txt")
p.write_text("Hello\n")
print(p.read_text())
Handling File Errors
try:
with open("missing.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("File not found")
except PermissionError:
print("Permission denied")
except IsADirectoryError:
print("Path is a directory")
Binary Files
with open("original.jpg", "rb") as source:
image_data = source.read()
with open("copy.jpg", "wb") as dest:
dest.write(image_data)
A Complete Working Program
CSV in, analysis, CSV+JSON out:
import csv
import json
from datetime import datetime
def read_student_scores(filepath):
students = []
with open(filepath, "r", newline="") as file:
reader = csv.DictReader(file)
for row in reader:
students.append({
"name": row["name"].strip(),
"scores": [int(x) for x in row["scores"].split("|")],
"course": row.get("course", "General"),
})
return students
def analyse_students(students):
results = []
for s in students:
avg = sum(s["scores"]) / len(s["scores"])
grade = "A" if avg >= 90 else "B" if avg >= 80 else "C" if avg >= 70 else "D" if avg >= 60 else "F"
results.append({
"name": s["name"],
"course": s["course"],
"average": round(avg, 1),
"grade": grade,
"passed": avg >= 60,
})
return sorted(results, key=lambda x: x["average"], reverse=True)
5 File Handling Mistakes Every Beginner Makes
- Opening files without
withand leaking handles - Using
wwhen you meanta - Forgetting to strip trailing newlines from lines
- Hardcoding path separators instead of
pathlib - Reading huge files entirely into memory unnecessarily
Practice Exercises
- Basic read/write with line numbers and word count
- CSV inventory value calculator
- JSON config loader/updater/reset
- Log analyzer for INFO/WARNING/ERROR
- Recursive file search utility with pathlib
-> See all Python File Handling exercises with solutions
What Comes Next - Day 15: Exception Handling
Day 15 dives deeper into robust error control:
try/except/else/finally- specific vs broad exceptions
- raising exceptions
- custom exception classes
- exception chaining
- exceptions vs return-value signaling
-> Continue to Day 15: Exception Handling
Frequently Asked Questions
What is file handling in Python?
Reading and writing persistent data on disk through file objects.
Why use with for file handling?
It guarantees file closure and prevents leaked resources.
Difference between r, w, and a modes?
r reads existing files, w overwrites, a appends.
Difference between read, readline, readlines?
Entire text, single line, or list of all lines respectively.
How do I work with CSV in Python?
Use the csv module, preferably DictReader/DictWriter.
How do I work with JSON in Python?
Use json.load/json.dump for files and loads/dumps for strings.
Why use pathlib?
Cleaner, cross-platform path operations with object-oriented API.
How do I handle file errors?
Catch specific exceptions like FileNotFoundError, PermissionError, and IsADirectoryError.
Chapter navigation
- Previous: Day 13: Modules and Packages
- Next: Day 15: Exception Handling
- Python Quiz: Take the Python quiz
- All Python exercises: Explore Python exercises
pathlib — The Modern Way to Handle Paths
The pathlib.Path object replaces all the clunky os.path string concatenation with clean object-oriented APIs.
from pathlib import Path
# Create paths with / operator — readable and cross-platform
base = Path('/var/data')
report = base / '2026' / 'june' / 'report.csv'
# Common operations
print(report.name) # 'report.csv'
print(report.stem) # 'report'
print(report.suffix) # '.csv'
print(report.parent) # /var/data/2026/june
print(report.parts) # ('/', 'var', 'data', '2026', 'june', 'report.csv')
# File operations
report.parent.mkdir(parents=True, exist_ok=True) # Create directories
report.write_text("date,sales
2026-06-01,1500") # Write entire file
content = report.read_text(encoding='utf-8') # Read entire file
report.rename(report.parent / 'report_final.csv') # Rename/move
# Discovery
for csv_file in Path('.').glob('**/*.csv'): # Recursive glob
print(csv_file)
# Path properties
path = Path('~/Documents/data.json').expanduser() # Resolve ~
abs_path = path.resolve() # Absolute path
relative = path.relative_to(Path.home()) # Relative to home
Binary File Handling
Not all files are text. Images, PDFs, executables and serialized data are binary.
# Write binary data
with open('image_copy.png', 'wb') as f: # 'wb' = write binary
with open('original.png', 'rb') as src: # 'rb' = read binary
f.write(src.read())
# Reading in chunks (for large files)
def copy_file_chunked(src_path, dst_path, chunk_size=65536):
with open(src_path, 'rb') as src, open(dst_path, 'wb') as dst:
while chunk := src.read(chunk_size):
dst.write(chunk)
# Struct — pack/unpack binary data (network protocols, file headers)
import struct
# Pack: 2-byte unsigned short + 4-byte float
packed = struct.pack('>Hf', 42, 3.14) # Big-endian
count, value = struct.unpack('>Hf', packed) # Unpack back
# BytesIO — in-memory binary file
from io import BytesIO
buffer = BytesIO()
buffer.write(b'hello world')
buffer.seek(0)
print(buffer.read()) # b'hello world'
Working with ZIP and Compressed Files
import zipfile
from pathlib import Path
# Create a zip archive
with zipfile.ZipFile('archive.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zf:
zf.write('report.csv') # Add single file
for p in Path('data').glob('**/*.json'):
zf.write(p, arcname=str(p.relative_to('data'))) # Preserve structure
# Read and extract
with zipfile.ZipFile('archive.zip', 'r') as zf:
print(zf.namelist()) # List all files
zf.extractall('output_dir') # Extract all
content = zf.read('report.csv') # Read without extracting
# Check and selectively extract
with zipfile.ZipFile('archive.zip') as zf:
for info in zf.infolist():
if info.filename.endswith('.json'):
zf.extract(info, 'json_only/')
File Watching and Temp Files
import tempfile
import os
# Temporary files — auto-deleted when closed/context exits
with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=True) as tmp:
tmp.write('id,name
1,Alice
')
tmp.flush()
print(f"Temp file: {tmp.name}")
# Process the temp file here
# File deleted automatically here
# Temporary directory
with tempfile.TemporaryDirectory() as tmpdir:
work_path = Path(tmpdir) / 'intermediate.bin'
work_path.write_bytes(b'processing...')
# All files in tmpdir deleted automatically
# Atomic writes — write to temp then rename (prevents partial writes)
def atomic_write(target_path: Path, content: str):
tmp = target_path.with_suffix('.tmp')
tmp.write_text(content, encoding='utf-8')
tmp.replace(target_path) # Atomic on POSIX; near-atomic on Windows
Reading Different File Encodings
# Read with explicit encoding (always specify for portability)
with open('data.csv', encoding='utf-8') as f:
content = f.read()
# Handle files with BOM (Byte Order Mark)
with open('windows_export.csv', encoding='utf-8-sig') as f:
content = f.read() # BOM stripped automatically
# Safe multi-encoding read attempt
def read_file_smart(path):
for enc in ['utf-8', 'utf-8-sig', 'cp1252', 'latin-1']:
try:
with open(path, encoding=enc) as f:
return f.read()
except UnicodeDecodeError:
continue
# Last resort: replace undecodable bytes with ?
with open(path, encoding='utf-8', errors='replace') as f:
return f.read()
# Common encodings
# utf-8: Default for modern web, JSON, Python source
# utf-16: Windows Unicode files (has BOM)
# latin-1: ISO-8859-1, Western European legacy
# cp1252: Windows Western European (superset of latin-1)
ConfigParser — INI Configuration Files
import configparser
# Config file (config.ini):
# [database]
# host = localhost
# port = 5432
# name = myapp
config = configparser.ConfigParser()
config.read('config.ini', encoding='utf-8')
host = config.get('database', 'host', fallback='localhost')
port = config.getint('database', 'port', fallback=5432)
debug = config.getboolean('app', 'debug', fallback=False)
# Write config
config['logging'] = {'level': 'INFO', 'file': 'app.log'}
with open('config.ini', 'w') as f:
config.write(f)
File Walking and Directory Operations
from pathlib import Path
import shutil
project = Path('my_project')
# Walk all files recursively
for file in project.rglob('*.py'):
size = file.stat().st_size
modified = file.stat().st_mtime
print(f"{file.relative_to(project)} — {size} bytes")
# Copy, move, delete with shutil
shutil.copy('source.txt', 'dest.txt') # Copy file
shutil.copytree('src_dir', 'dst_dir') # Copy entire directory
shutil.move('old_location.txt', 'new_location.txt') # Move/rename
shutil.rmtree('temp_directory') # Delete directory tree
# Get disk usage
usage = shutil.disk_usage('/')
print(f"Free: {usage.free / 1e9:.1f} GB / Total: {usage.total / 1e9:.1f} GB")
Excel and Spreadsheet Files
# openpyxl — read/write .xlsx files (pip install openpyxl)
import openpyxl
# Write a spreadsheet
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Sales"
# Headers
ws.append(["Date", "Product", "Quantity", "Price", "Total"])
# Data rows
data = [("2026-06-01", "Widget A", 100, 9.99, 999.0),
("2026-06-02", "Widget B", 50, 14.99, 749.5)]
for row in data:
ws.append(row)
# Formatting
from openpyxl.styles import Font, PatternFill
header_row = ws[1]
for cell in header_row:
cell.font = Font(bold=True)
wb.save("sales_report.xlsx")
# Read a spreadsheet
wb = openpyxl.load_workbook("sales_report.xlsx")
ws = wb.active
for row in ws.iter_rows(min_row=2, values_only=True): # Skip header
date, product, qty, price, total = row
print(f"{product}: {qty} units @ {price:.2f} = {total:.2f}")
# xlrd — read older .xls files (pip install xlrd)
# pandas — for data analysis with DataFrames (pip install pandas)
import pandas as pd
df = pd.read_excel("sales_report.xlsx")
print(df.describe())
Environment-Aware File Paths
from pathlib import Path
import os
def get_data_dir() -> Path:
"""Return the appropriate data directory based on the environment."""
# Check explicit override first
if env_path := os.getenv('DATA_DIR'):
return Path(env_path)
# Production: use /var/data or similar
if os.getenv('ENVIRONMENT') == 'production':
return Path('/var/app/data')
# Development: use local directory
return Path(__file__).parent.parent / 'data'
DATA_DIR = get_data_dir()
DATA_DIR.mkdir(parents=True, exist_ok=True)
# Usage throughout the app
users_file = DATA_DIR / 'users.json'
cache_dir = DATA_DIR / 'cache'
Frequently asked questions: File handling
What is file handling in Python?
File handling means reading from and writing to files so data persists beyond program execution.
What is the with statement in Python file handling?
with open(...) automatically closes files and is the recommended safe pattern.
What is the difference between 'r', 'w', and 'a' modes?
'r' reads existing files, 'w' overwrites or creates, and 'a' appends while preserving content.
What is the difference between read(), readline(), and readlines()?
read() returns full content, readline() returns one line, readlines() returns a list of lines.
How do I read a CSV file in Python?
Use csv.DictReader for header-based row access and open files with newline="" for compatibility.
How do I read and write JSON files in Python?
Use json.load/json.dump for files and json.loads/json.dumps for strings.
What is pathlib and why should I use it?
pathlib provides modern, readable, cross-platform path handling with Path objects.
How do I handle file errors in Python?
Catch specific exceptions like FileNotFoundError, PermissionError, and IsADirectoryError.
Related Page
Day 13: Modules and Packages
Learn about day 13: modules and packages
Learn MoreDay 15: Exception Handling
Continue with day 15: exception handling
Learn MorePython exercises hub
165+ programs with solutions
Learn MorePython quiz
Multiple-choice checks with explanations
Learn MorePython file handling programs
File operations, CSV, and JSON practice programs
Learn More