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

ModeMeaning
'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

  1. Write a program that saves a list of names to a file, one per line.
  2. Read the file back and count how many names it contains.
  3. Write a program that appends new entries to an existing log file.
  4. Read a JSON file into a Python dict and print all keys and values.
  5. Write a CSV with at least 3 columns and read it back with csv.DictReader.

Review Questions

  1. Why should you always use the with statement when opening files?
  2. What is the difference between 'w' and 'a' mode?
  3. Why is iterating over a file with for line in file: better than readlines() for large files?
  4. What does json.load() do vs json.loads()?
  5. 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 csv module
  • I can read and write JSON files with the json module
  • I can use pathlib.Path for cross-platform file paths