File Handling in Python
Reviewed & published by Brayan K
Learn to read, write, and manage files to save data permanently.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- Why files are essential for saving data permanently
- File modes: read (r), write (w), append (a), create (x)
- How to safely open files using the with statement
- How to read entire files, line by line, or as a list
- How to write and append to files
- How to check if a file exists and work with CSV/JSON
1. Why Learn File Handling?
When your program ends, all variables are lost. Files let you save data permanently so it survives after your program closes.
What Can You Do with Files?
- Save user data — Store settings, preferences, scores
- Read data — Load configuration, import information
- Create logs — Track events and errors
- Process data — Work with CSV, JSON, or text files
2. Opening Files with open()
Use the open() function to work with files. You specify the filename and a mode that tells Python what you want to do.
| Mode | Name | What It Does |
|---|---|---|
| "r" | Read | Open for reading (file must exist) |
| "w" | Write | Create new or overwrite existing |
| "a" | Append | Add to end (keeps existing content) |
| "x" | Create | Create new (error if file exists) |
3. The with Statement (Best Practice)
Always use the with statement when working with files. It automatically closes the file when you are done, even if an error occurs.
file = open("data.txt", "r")
content = file.read()
file.close() # Easy to forget!with open("data.txt", "r") as file:
content = file.read()
# Automatically closed!4. Reading Files
There are several ways to read file content:
| Method | What It Returns | Best For |
|---|---|---|
| .read() | Entire file as one string | Small files |
| .readline() | One line at a time | Processing line by line |
| .readlines() | List of all lines | When you need a list |
| for line in file: | Each line (loop) | Large files (memory efficient) |
# A string standing in for a file, so you can see the SHAPE of each result
# (the worked example further down does all of this with a real file)
file_content = """Line 1: Hello Python!
Line 2: File handling is useful.
Line 3: Keep practicing!"""
# Simulate .read() - entire file
print("=== read() - Entire file ===")
print(file_content)
# Simulate .readlines() - list of lines
print("\n=== readlines() - List of lines ===")
lines = file_content.split("\n")
print(lines)
# Simulate looping through lines
print("\n=== Loop through lines ===")
for i, line in enumerate(lines, 1):
print(f"Line {i}: {line}")
# ✅ Expected output:
# === read() - Entire file ===
# Line 1: Hello Python!
# Line 2: File handling is useful.
# Line 3: Keep practicing!
#
# === readlines() - List of lines ===
# ['Line 1: Hello Python!', 'Line 2: File handling is useful.', 'Line 3: Keep practicing!']
#
# === Loop through lines ===
# Line 1: Line 1: Hello Python!
# Line 2: Line 2: File handling is useful.
# Line 3: Line 3: Keep practicing!5. Writing to Files
Use mode "w" to write to a file. This creates a new file or overwrites an existing one.
# Simulating file writing
output = []
# Simulate writing lines
output.append("Hello, World!")
output.append("This is line 2.")
output.append("Python file handling is easy!")
print("=== File would contain ===")
for line in output:
print(line)
# Real code would be:
# with open("output.txt", "w") as file:
# file.write("Hello, World!\n")
# file.write("This is line 2.\n")
# ✅ Expected output:
# === File would contain ===
# Hello, World!
# This is line 2.
# Python file handling is easy!6. Appending to Files
Use mode "a" to add content to the end of a file without deleting existing content.
# Simulating append behavior
existing_content = [
"Log entry 1: Started app",
"Log entry 2: User logged in"
]
print("=== Existing content ===")
for line in existing_content:
print(line)
# Append new entries
existing_content.append("Log entry 3: New action performed")
existing_content.append("Log entry 4: User logged out")
print("\n=== After appending ===")
for line in existing_content:
print(line)
# ✅ Expected output:
# === Existing content ===
# Log entry 1: Started app
# Log entry 2: User logged in
#
# === After appending ===
# Log entry 1: Started app
# Log entry 2: User logged in
# Log entry 3: New action performed
# Log entry 4: User logged out| Mode | Existing Content | Use Case |
|---|---|---|
| "w" | Deleted (overwritten) | Creating new files, saving fresh data |
| "a" | Kept (added to) | Logs, history, collecting data |
7. Checking if a File Exists
Before reading a file, check if it exists to avoid errors:
import os
# Check if file exists
filename = "data.txt"
# Simulating os.path.exists behavior
files_on_disk = ["notes.txt", "config.json", "data.txt"]
if filename in files_on_disk:
print(f"'{filename}' exists! Safe to open.")
else:
print(f"'{filename}' not found.")
# Real code:
# if os.path.exists("data.txt"):
# with open("data.txt", "r") as file:
# content = file.read()
# else:
# print("File not found")
# ✅ Expected output:
# 'data.txt' exists! Safe to open.8. Handling File Errors
Use try-except to handle errors gracefully when working with files:
| Error | Cause |
|---|---|
| FileNotFoundError | File does not exist (reading mode) |
| PermissionError | No permission to access file |
| IsADirectoryError | Tried to open a folder as a file |
# Safe file reading with try-except
def read_file_safely(filename):
try:
# Simulate file operations
if filename == "missing.txt":
raise FileNotFoundError(f"No such file: '{filename}'")
elif filename == "protected.txt":
raise PermissionError(f"Access denied: '{filename}'")
else:
return f"Content of {filename}: Hello World!"
except FileNotFoundError:
return "Error: File not found."
except PermissionError:
return "Error: Permission denied."
# Test different scenarios
print(read_file_safely("data.txt"))
print(read_file_safely("missing.txt"))
print(read_file_safely("protected.txt"))
# ✅ Expected output:
# Content of data.txt: Hello World!
# Error: File not found.
# Error: Permission denied.🟢 Worked Example: a real file, start to finish
The examples above stand in for files with strings and lists so you can see the shapes involved. This one uses a real file: it creates diary.txt, reads it back three different ways, appends to it, deliberately trips over a missing file, and deletes what it made. Nothing is pretend, and nothing is left behind.
# 🟢 WORKED EXAMPLE — create a real file, read it back, then clean up
import os
# ---------- 1. Write ----------
# "w" creates the file, or empties it if it already exists.
# The with block closes the file for you as soon as you leave it.
with open("diary.txt", "w") as file:
file.write("Monday: started Python\n") # write() adds NO line break — you do
file.write("Tuesday: learned lists\n")
print("File created?", os.path.exists("diary.txt"))
# ---------- 2. Read the whole thing ----------
with open("diary.txt", "r") as file:
everything = file.read() # one long string, newlines included
print("=== read() gives one big string ===")
print(everything)
# ---------- 3. Read it as a list of lines ----------
with open("diary.txt") as file: # no mode given, so "r" is assumed
lines = file.readlines()
print("=== readlines() gives a list ===")
print(lines) # look closely: each item still ends in \n
# ---------- 4. Loop through it, one line at a time ----------
print("=== looping, tidied up with .strip() ===")
with open("diary.txt") as file:
for number, line in enumerate(file, start=1):
print(f"{number}. {line.strip()}") # strip() removes the trailing newline
# ---------- 5. Append (mode "a" keeps what is already there) ----------
with open("diary.txt", "a") as file:
file.write("Wednesday: read a file\n")
with open("diary.txt") as file:
print("Lines now:", len(file.readlines()))
# ---------- 6. What a missing file actually looks like ----------
try:
with open("missing.txt") as file:
file.read()
except FileNotFoundError as err:
print("FileNotFoundError:", err)
# ---------- 7. Tidy up ----------
os.remove("diary.txt")
print("Cleaned up. Still there?", os.path.exists("diary.txt"))
# ✅ Expected output:
# File created? True
# === read() gives one big string ===
# Monday: started Python
# Tuesday: learned lists
#
# === readlines() gives a list ===
# ['Monday: started Python\n', 'Tuesday: learned lists\n']
# === looping, tidied up with .strip() ===
# 1. Monday: started Python
# 2. Tuesday: learned lists
# Lines now: 3
# FileNotFoundError: [Errno 2] No such file or directory: 'missing.txt'
# Cleaned up. Still there? FalseNotice the blank line in the middle of the expected output. It is not a mistake: the file ends with a newline, and print() adds another one.
🎯 Your Turn: a scoreboard file
Three blanks, and the first two are the choice that trips everyone up at least once: which mode wipes the file, and which one adds to it.
# 🎯 YOUR TURN — fill in the blanks marked with ___
import os
# 1) Create scores.txt and put the first two results in it.
with open("scores.txt", "___") as file: # 👉 the mode that creates a fresh file
file.write("Alice 10\n")
file.write("Bob 8\n")
# 2) A late result arrives. Add it WITHOUT wiping the first two.
with open("scores.txt", "___") as file: # 👉 the mode that adds to the end
file.write("Cara 12\n")
# Read the whole scoreboard back — already written for you.
with open("scores.txt") as file:
lines = file.readlines()
print("Lines in file:", len(lines))
for line in lines:
name, points = line.split() # "Alice 10" -> ["Alice", "10"]
print(f"{name} scored {points}")
# 3) Delete the file so a re-run starts clean.
os.___("scores.txt") # 👉 which os function deletes a file?
print("File still there?", os.path.exists("scores.txt"))
# ✅ Expected output:
# Lines in file: 3
# Alice scored 10
# Bob scored 8
# Cara scored 12
# File still there? FalseIf "Lines in file" comes out as 1, blank 2 is the problem: you opened the file in a mode that emptied it before writing, so Alice and Bob were thrown away.
9. Working with CSV Files
CSV (Comma-Separated Values) is a common format for spreadsheet data. Python has a built-in csv module to work with it.
import csv
from io import StringIO
# Create CSV data in memory
csv_data = StringIO()
writer = csv.writer(csv_data)
writer.writerow(["Name", "Age", "City"])
writer.writerow(["Alice", 25, "London"])
writer.writerow(["Bob", 30, "Paris"])
writer.writerow(["Charlie", 35, "Tokyo"])
# Read the CSV data
csv_data.seek(0)
print("=== CSV Content ===")
reader = csv.reader(csv_data)
for row in reader:
print(row)
# Using DictReader for easier access
csv_data.seek(0)
print("\n=== As Dictionaries ===")
dict_reader = csv.DictReader(csv_data)
for row in dict_reader:
print(f"{row['Name']} is {row['Age']} years old")
# ✅ Expected output:
# === CSV Content ===
# ['Name', 'Age', 'City']
# ['Alice', '25', 'London']
# ['Bob', '30', 'Paris']
# ['Charlie', '35', 'Tokyo']
#
# === As Dictionaries ===
# Alice is 25 years old
# Bob is 30 years old
# Charlie is 35 years old10. Working with JSON Files
JSON (JavaScript Object Notation) is used for configuration files and APIs. Python can easily convert between JSON and dictionaries.
| Function | What It Does |
|---|---|
| json.dumps(obj) | Convert Python dict → JSON string |
| json.loads(str) | Convert JSON string → Python dict |
| json.dump(obj, file) | Write dict to JSON file |
| json.load(file) | Read JSON file to dict |
import json
# Python dictionary
data = {
"name": "Alice",
"age": 25,
"skills": ["Python", "JavaScript"],
"active": True
}
# Convert to JSON string (for saving to file)
json_string = json.dumps(data, indent=2)
print("=== JSON String ===")
print(json_string)
# Convert back to Python dict (for reading from file)
parsed = json.loads(json_string)
print("\n=== Back to Python ===")
print(f"Name: {parsed['name']}")
print(f"Skills: {parsed['skills']}")
# ✅ Expected output:
# === JSON String ===
# {
# "name": "Alice",
# "age": 25,
# "skills": [
# "Python",
# "JavaScript"
# ],
# "active": true
# }
#
# === Back to Python ===
# Name: Alice
# Skills: ['Python', 'JavaScript']🏁 Mini-Challenge: save settings as real JSON
No blanks — a brief and an outline. Save a settings dictionary to a real settings.json file, print the raw text that ended up in it, load it straight back, and prove that what came back is identical to what went in.
Remember which function is which: json.dump(data, file) writes to a file, json.dumps(data) returns a string. The plural s is for "string". Same pair on the way back: json.load(file) versus json.loads(text).
# 🏁 MINI-CHALLENGE — write it yourself
import json
import os
settings = {"theme": "dark", "font_size": 14, "autosave": True}
# 1. Open settings.json for writing and json.dump(settings, file, indent=2)
# indent=2 makes the file readable by a human instead of one long line.
# 2. Open it again and print(file.read()) so you can see the raw text.
# 3. Open it once more and json.load(file) into a variable called loaded.
# 4. Print whether loaded == settings, then print loaded["theme"],
# then print type(loaded["autosave"]).__name__
# 5. os.remove the file and print whether it is gone.
# your code here
# ✅ Expected output:
# {
# "theme": "dark",
# "font_size": 14,
# "autosave": true
# }
#
# Same as the original? True
# Theme: dark
# Type of autosave: bool
# Cleaned up: True
#
# Look at the file text: Python's True was written as JSON's lowercase true.
# JSON is its own format with its own spelling — but it comes back as a real
# Python bool, which is what step 4 proves.11. Common Mistakes to Avoid
| Mistake | Problem | Solution |
|---|---|---|
| Forgetting to close file | Data may not save, file locked | Use with statement |
| Using "w" instead of "a" | Existing data deleted | Double-check your mode |
| Forgetting \n | All text on one line | Add \n after each line |
| Reading non-existent file | FileNotFoundError | Check with os.path.exists() |
| Wrong encoding | Strange characters appear | Add encoding="utf-8" |
12. Practical Examples
Example 1: Simple Note Saver
# Simple note saver simulation
notes = []
def save_note(note):
notes.append(note)
print(f"Saved: {note}")
def show_notes():
print("\n=== Your Notes ===")
for i, note in enumerate(notes, 1):
print(f"{i}. {note}")
# Add some notes
save_note("Buy groceries")
save_note("Call mom")
save_note("Finish Python lesson")
# Display all notes
show_notes()
# Real implementation:
# with open("notes.txt", "a") as f:
# f.write(note + "\n")
# ✅ Expected output:
# Saved: Buy groceries
# Saved: Call mom
# Saved: Finish Python lesson
#
# === Your Notes ===
# 1. Buy groceries
# 2. Call mom
# 3. Finish Python lessonExample 2: Word Counter
# Count words, lines, and characters in text
text = """Python is a great programming language.
It is easy to learn and very powerful.
Many developers love using Python for various projects."""
lines = text.split("\n")
words = text.split()
chars = len(text)
print("=== Text Analysis ===")
print(f"Lines: {len(lines)}")
print(f"Words: {len(words)}")
print(f"Characters: {chars}")
# Count word frequency
word_count = {}
for word in words:
word = word.lower().strip(".,!?")
word_count[word] = word_count.get(word, 0) + 1
print("\n=== Word Frequency ===")
for word, count in sorted(word_count.items()):
if count > 1:
print(f" '{word}': {count} times")
# ✅ Expected output:
# === Text Analysis ===
# Lines: 3
# Words: 22
# Characters: 134
#
# === Word Frequency ===
# 'is': 2 times
# 'python': 2 timesExample 3: Simple Log System
from datetime import datetime
# Simple logging system
log = []
def log_event(event):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
entry = f"[{timestamp}] {event}"
log.append(entry)
print(entry)
# Log some events
log_event("Application started")
log_event("User logged in")
log_event("File saved successfully")
log_event("User logged out")
print("\n=== Complete Log ===")
for entry in log:
print(entry)Summary: Quick Reference
| Operation | Code |
|---|---|
| Read entire file | with open("f.txt", "r") as f: text = f.read() |
| Read lines | for line in file: |
| Write (overwrite) | with open("f.txt", "w") as f: f.write("text") |
| Append | with open("f.txt", "a") as f: f.write("more") |
| Check exists | os.path.exists("f.txt") |
| Read CSV | csv.reader(file) |
| Read JSON | json.load(file) |
| Write JSON | json.dump(data, file) |
Lesson complete — your programs can now save and load data!
Reading, writing, appending, CSV, JSON — you know how to work with files safely using the with statement. Your programs can now remember things between runs.
Practice quiz
Which file mode opens a file for reading (and errors if it doesn't exist)?
- "w"
- "a"
- "r"
- "x"
Answer: "r". Mode "r" opens for reading; the file must already exist.
What does opening a file in mode 'w' do to existing content?
- Appends to the end
- Overwrites (deletes) it
- Raises an error
- Leaves it unchanged
Answer: Overwrites (deletes) it. Mode "w" creates a new file or overwrites an existing one, deleting its content.
Which mode adds to the end of a file without deleting existing content?
- "a"
- "w"
- "r"
- "x"
Answer: "a". Mode "a" (append) keeps existing content and adds to the end.
Why is 'with open(...) as file:' the recommended way to handle files?
- It runs faster
- It automatically closes the file, even if an error occurs
- It allows multiple modes at once
- It compresses the file
Answer: It automatically closes the file, even if an error occurs. The with statement auto-closes the file when the block ends, even on errors.
What does the .read() method return?
- A list of lines
- One line at a time
- The entire file as one string
- The number of characters
Answer: The entire file as one string. .read() returns the whole file content as a single string.
Which method returns a list of all lines in a file?
- .read()
- .readline()
- .readlines()
- .lines()
Answer: .readlines(). .readlines() returns a list where each element is one line.
Does .write() automatically add a newline at the end of each call?
- Yes, always
- No, you must add yourself
- Only in append mode
- Only for the first line
Answer: No, you must add yourself. .write() does not add line breaks; you add yourself for separate lines.
Which exception is raised when reading a file that doesn't exist?
- FileNotFoundError
- PermissionError
- ValueError
- KeyError
Answer: FileNotFoundError. Reading a missing file raises FileNotFoundError.
Which function checks whether a file exists before opening it?
- os.exists()
- os.path.exists()
- file.exists()
- os.check()
Answer: os.path.exists(). os.path.exists('file.txt') returns True if the path exists.
Which json function reads a JSON file directly into a Python object?
- json.loads(str)
- json.dumps(obj)
- json.load(file)
- json.dump(obj, file)
Answer: json.load(file). json.load(file) reads from an open file object into Python; loads is for strings.
Continue this course
- Previous: Checkpoint: A Real-World Script
- Next: Exception Handling — Handle errors gracefully so your programs don't crash unexpectedly
- Quick reference: Python cheat sheet › Files & with