File I/O
Python makes reading and writing files straightforward. Whether you are saving user data, reading configuration files, processing log files, or working with CSVs and JSON, Python's file I/O tools handle it all.
Why This Chapter Matters
Files are how programs store data that persists beyond a single run. Logs, configs, exports, and imports all involve reading and writing files.
Opening a File
Use open() to open a file. The best practice is to use the with statement, which guarantees the file is closed automatically — even if an error occurs.
with open("notes.txt", "r") as file:
content = file.read()
print(content)
Without with, you must manually call file.close(), which is easy to forget.
File Modes
| Mode | Meaning |
|---|---|
'r' | Read (default) — error if file doesn't exist |
'w' | Write — creates or OVERWRITES the file |
'a' | Append — adds to end without overwriting |
'x' | Exclusive create — error if file already exists |
'b' | Binary mode (combine: 'rb', 'wb') |
'+' | Read and write (combine: 'r+', 'w+') |
Writing to Files
Write (Overwrite)
with open("output.txt", "w") as file:
file.write("Line 1\n")
file.write("Line 2\n")
Append
with open("log.txt", "a") as file:
file.write("New log entry\n")
Write Multiple Lines
lines = ["Asha\n", "Leo\n", "Mina\n"]
with open("names.txt", "w") as file:
file.writelines(lines)
Reading from Files
Read Everything
with open("notes.txt", "r") as file:
content = file.read()
print(content)
Read Lines as a List
with open("notes.txt", "r") as file:
lines = file.readlines()
for line in lines:
print(line.strip()) # .strip() removes the \n
Read One Line at a Time (Memory Efficient)
with open("large_file.txt", "r") as file:
for line in file: # iterate directly — best for large files
print(line.strip())
Read a Single Line
with open("notes.txt", "r") as file:
first_line = file.readline()
Working with File Paths
Use the pathlib module (recommended in modern Python) or os.path:
from pathlib import Path
# Create a Path object
data_dir = Path("data")
file_path = data_dir / "notes.txt"
# Check existence
print(file_path.exists())
print(file_path.is_file())
print(file_path.is_dir())
# Create directory
data_dir.mkdir(exist_ok=True)
# List files in a directory
for file in Path(".").iterdir():
print(file.name)
# Read and write with pathlib
text = file_path.read_text(encoding="utf-8")
file_path.write_text("New content", encoding="utf-8")
Always specify encoding="utf-8" when working with text files for cross-platform safety.
Working with CSV Files
CSV (Comma-Separated Values) is a common format for tabular data.
Reading CSV
import csv
with open("students.csv", "r", newline="") as file:
reader = csv.DictReader(file)
for row in reader:
print(row["name"], row["score"])
Writing CSV
import csv
students = [
{"name": "Asha", "score": 95},
{"name": "Leo", "score": 88}
]
with open("output.csv", "w", newline="") as file:
writer = csv.DictWriter(file, fieldnames=["name", "score"])
writer.writeheader()
writer.writerows(students)
Working with JSON Files
JSON is a standard format for configuration files and API data.
Reading JSON
import json
with open("config.json", "r") as file:
config = json.load(file) # dict
print(config["database"]["host"])
Writing JSON
import json
data = {
"name": "Nandhoo",
"version": "1.0",
"features": ["python", "javascript"]
}
with open("config.json", "w") as file:
json.dump(data, file, indent=4)
Convert Between JSON and String
import json
# Dict to JSON string
json_string = json.dumps(data, indent=2)
print(json_string)
# JSON string to dict
parsed = json.loads(json_string)
Checking if a File Exists
from pathlib import Path
if Path("data.txt").exists():
print("File found")
else:
print("File not found")
Common Patterns
Safe File Read with Error Handling
from pathlib import Path
def read_file(filename):
try:
return Path(filename).read_text(encoding="utf-8")
except FileNotFoundError:
print(f"Error: '{filename}' not found.")
return None
except PermissionError:
print(f"Error: No permission to read '{filename}'.")
return None
Process Each Line
results = []
with open("data.txt", "r") as file:
for line in file:
value = line.strip()
if value: # skip empty lines
results.append(value)
Common Mistakes
- not using
with(forgetting to close the file) - using
'w'mode when you meant'a'(overwrites data!) - not stripping newlines from
readlines()results - not specifying encoding, leading to cross-platform issues
- reading a huge file entirely into memory instead of streaming line by line
Mini Exercises
- Write a program that saves a list of names to a file, one per line.
- Read the file back and count how many names it contains.
- Write a program that appends new entries to an existing log file.
- Read a JSON file into a Python dict and print all keys and values.
- Write a CSV with at least 3 columns and read it back with
csv.DictReader.
Review Questions
- Why should you always use the
withstatement when opening files? - What is the difference between
'w'and'a'mode? - Why is iterating over a file with
for line in file:better thanreadlines()for large files? - What does
json.load()do vsjson.loads()? - Why should you specify
encoding="utf-8"when opening text files?
Reference Checklist
- I can read and write files using
with open() - I know all common file modes and when to use each
- I can process files line by line efficiently
- I can read and write CSV files with the
csvmodule - I can read and write JSON files with the
jsonmodule - I can use
pathlib.Pathfor cross-platform file paths