Last updated: 2026-05-22

Python file IO

File I/O (Input/Output) is how Python reads data from files and writes data to them. It is one of the most fundamental ways a program interacts with the outside world — reading configuration files, processing data, logging output, saving results, and working with structured formats like CSV and JSON. Python's built-in tools for file handling are clean and straightforward.


Opening a File — open()

The open() function is the entry point for all file operations. It returns a file object that you use to read or write:

file = open("filename.txt", "r")

File modes

The second argument to open() is the mode — it determines what you can do with the file:

ModeMeaning
"r"Read (default). File must exist.
"w"Write. Creates the file if it doesn't exist; overwrites if it does.
"a"Append. Creates if needed; adds to the end without overwriting.
"x"Exclusive creation. Fails if the file already exists.
"r+"Read and write. File must exist.
"b"Binary mode — add to another mode: "rb", "wb".
"t"Text mode (default) — add to another mode: "rt", "wt".

The with Statement — Always Use It

The recommended way to work with files is the with statement. It automatically closes the file when the block exits — even if an error occurs:

with open("data.txt", "r") as file:
    content = file.read()

# File is automatically closed here

Without with, you must close the file manually — and if an exception occurs before file.close(), the file stays open, which can cause data loss or resource leaks:

# Without with — not recommended
file = open("data.txt", "r")
content = file.read()
file.close()    # Easy to forget, skipped on exceptions

Always use with open(...).


Reading Files

Read the entire file — .read()

with open("data.txt", "r") as file:
    content = file.read()
    print(content)

Returns the entire file as a single string.

Read line by line — .readline()

with open("data.txt", "r") as file:
    line = file.readline()    # Reads one line at a time
    while line:
        print(line, end="")   # end="" avoids double newlines
        line = file.readline()

Read all lines into a list — .readlines()

with open("data.txt", "r") as file:
    lines = file.readlines()    # ["line 1\n", "line 2\n", "line 3\n"]

for line in lines:
    print(line.strip())    # strip() removes the trailing \n

Iterate directly over the file — most Pythonic

with open("data.txt", "r") as file:
    for line in file:
        print(line.strip())

Iterating directly over the file object is memory-efficient — it reads one line at a time without loading the entire file into memory. This is the preferred approach for large files.


Writing Files

Write to a file — "w" mode

with open("output.txt", "w") as file:
    file.write("Hello, World!\n")
    file.write("Second line\n")

⚠️ Opening a file in "w" mode overwrites any existing content. The file is truncated to zero length when opened.

Write multiple lines at once — .writelines()

lines = ["Line one\n", "Line two\n", "Line three\n"]

with open("output.txt", "w") as file:
    file.writelines(lines)

Note that .writelines() does not add newlines automatically — include \n in each string.

Append to a file — "a" mode

with open("log.txt", "a") as file:
    file.write("New log entry\n")

Opening in "a" mode adds content to the end of the file without touching what is already there.


Working with File Paths — pathlib

The pathlib module provides an object-oriented interface for file paths that is cleaner and more readable than string concatenation:

from pathlib import Path

# Building paths safely (works on Windows, macOS, and Linux)
base = Path("my_project")
data_file = base / "data" / "input.txt"

print(data_file)    # my_project/data/input.txt

# Reading with pathlib
content = data_file.read_text()

# Writing with pathlib
output = Path("output.txt")
output.write_text("Hello from pathlib")

Useful pathlib operations

p = Path("my_project/data/input.txt")

p.exists()          # True if the path exists
p.is_file()         # True if it is a file
p.is_dir()          # True if it is a directory
p.name              # "input.txt" — filename with extension
p.stem              # "input" — filename without extension
p.suffix            # ".txt" — file extension
p.parent            # Path("my_project/data") — parent directory

# Creating directories
Path("new_folder").mkdir(exist_ok=True)
Path("a/b/c").mkdir(parents=True, exist_ok=True)  # Creates all parents

# Listing directory contents
for file in Path(".").iterdir():
    print(file)

# Glob — find files matching a pattern
for txt_file in Path(".").glob("*.txt"):
    print(txt_file)

Working with CSV Files

CSV (Comma-Separated Values) is one of the most common data formats. Python's csv module handles the parsing correctly — including quoted fields and commas within values:

Reading CSV

import csv

with open("users.csv", "r", newline="") as file:
    reader = csv.reader(file)
    header = next(reader)    # Skip the header row
    for row in reader:
        print(row)    # Each row is a list of strings

Reading CSV as dictionaries

import csv

with open("users.csv", "r", newline="") as file:
    reader = csv.DictReader(file)
    for row in reader:
        print(row["name"], row["age"])    # Access by column name

Writing CSV

import csv

users = [
    ["name", "age", "city"],
    ["Wariz", "20", "Lagos"],
    ["Ada", "25", "Abuja"]
]

with open("users.csv", "w", newline="") as file:
    writer = csv.writer(file)
    writer.writerows(users)

Working with JSON Files

JSON is covered fully in the JSON topic. Here is the file-specific pattern:

import json

# Reading JSON from a file
with open("data.json", "r") as file:
    data = json.load(file)    # Parses JSON and returns a Python object

# Writing JSON to a file
user = {"name": "Wariz", "age": 20, "city": "Lagos"}

with open("data.json", "w") as file:
    json.dump(user, file, indent=4)    # indent for pretty-printing

Handling File Errors

File operations can fail — the file may not exist, permissions may be denied, or the disk may be full. Always handle these cases:

try:
    with open("data.txt", "r") as file:
        content = file.read()
except FileNotFoundError:
    print("File does not exist.")
except PermissionError:
    print("Permission denied.")
except OSError as e:
    print(f"OS error: {e}")

Common file-related exceptions:

ExceptionWhen it occurs
FileNotFoundErrorFile or directory does not exist
PermissionErrorInsufficient permissions to read or write
FileExistsErrorFile already exists (raised by "x" mode)
IsADirectoryErrorTried to open a directory as a file
OSErrorGeneral OS-level error — catches all of the above

Checking if a File Exists Before Opening

from pathlib import Path

path = Path("data.txt")

if path.exists():
    content = path.read_text()
else:
    print("File not found")

This avoids the exception entirely when you just need a conditional check.


Reading and Writing Binary Files

For non-text files — images, PDFs, compiled binaries — use binary mode:

# Reading a binary file
with open("image.png", "rb") as file:
    data = file.read()    # Returns bytes, not a string

# Writing a binary file
with open("copy.png", "wb") as file:
    file.write(data)

Binary mode reads and writes raw bytes. Do not use text mode for binary files — it will corrupt them on Windows, which translates newline characters.


Summary

OperationCode
Open for readingopen("file.txt", "r")
Open for writing (overwrite)open("file.txt", "w")
Open for appendingopen("file.txt", "a")
Always use withwith open(...) as f:
Read entire filef.read()
Read line by linefor line in f:
Read all lines to listf.readlines()
Write a stringf.write("text")
Write multiple linesf.writelines(list)
Build a path safelyPath("folder") / "file.txt"
Check if file existsPath("file").exists()
Read CSV with headerscsv.DictReader(f)
Read JSON from filejson.load(f)
Write JSON to filejson.dump(data, f, indent=4)