Skip to content

Advanced File Handling in Python

This lesson continues from File Handling Basics and covers the file features used in real projects: more modes, moving the file cursor, encodings, binary files, CSV and JSON, folders, copying files, pathlib and writing your own context manager.

Setup

import os
import csv
import json
import shutil
from pathlib import Path

folder = "practice_files"
os.makedirs(folder, exist_ok=True)   # create folder, no error if it exists

1. More modes

print("1. More file modes")

# "x" -> create a new file, error if it already exists
try:
    with open(f"{folder}/new.txt", "x") as file:
        file.write("Created with x mode\n")
    print("new.txt created")
except FileExistsError:
    print("new.txt already exists, x mode does not overwrite")

# "w+" -> write and read (erases old content)
with open(f"{folder}/notes.txt", "w+") as file:
    file.write("Line 1\nLine 2\n")
    file.seek(0)                     # go back to start before reading
    print("w+ read:", file.read().split())

# "r+" -> read and write, file must exist, does NOT erase content
with open(f"{folder}/notes.txt", "r+") as file:
    print("r+ read:", file.read().split())
    file.write("Line 3\n")           # cursor is at the end, so it adds here

# "a+" -> append and read
with open(f"{folder}/notes.txt", "a+") as file:
    file.write("Line 4\n")
    file.seek(0)
    print("a+ read:", file.read().split())

2. seek() and tell()

print("\n2. seek() and tell()")
with open(f"{folder}/notes.txt", "r") as file:
    print("Cursor at start:", file.tell())
    file.read(6)                     # read 6 characters
    print("After reading 6 chars:", file.tell())
    file.seek(0)                     # move cursor back to start
    print("After seek(0):", file.tell())

3. Encoding

Use encoding="utf-8" for Hindi, emojis or other special characters.

print("\n3. Encoding")
message = "नमस्ते Python"
with open(f"{folder}/hindi.txt", "w", encoding="utf-8") as file:
    file.write(message)
with open(f"{folder}/hindi.txt", "r", encoding="utf-8") as file:
    text = file.read()
# We compare instead of print, because some Windows terminals can't show Hindi
print("Hindi text saved and read back correctly:", text == message)

4. Binary files

"b" mode reads/writes bytes - used for images, PDFs, videos etc.

print("\n4. Binary files")
with open(f"{folder}/data.bin", "wb") as file:
    file.write(bytes([65, 66, 67, 68]))      # bytes of A B C D

with open(f"{folder}/data.bin", "rb") as source:
    with open(f"{folder}/data_copy.bin", "wb") as target:
        target.write(source.read())          # copy byte by byte

with open(f"{folder}/data_copy.bin", "rb") as file:
    print("Copied bytes:", file.read())

5. Read a big file in chunks

For very big files, don't use read() - read small parts instead.

print("\n5. Reading in chunks of 10 characters")
with open(f"{folder}/notes.txt", "r") as file:
    while True:
        chunk = file.read(10)
        if not chunk:                # empty string means end of file
            break
        print(repr(chunk))

6. CSV files

print("\n6. CSV files")
students = [
    ["Name", "Age", "City"],
    ["Sudheer", 25, "Pune"],
    ["Divyani", 22, "Mumbai"],
]
# newline="" avoids blank lines between rows on Windows
with open(f"{folder}/students.csv", "w", newline="") as file:
    writer = csv.writer(file)
    writer.writerows(students)

with open(f"{folder}/students.csv", "r") as file:
    reader = csv.DictReader(file)    # each row becomes a dictionary
    for row in reader:
        print(row["Name"], "is", row["Age"], "years old from", row["City"])

7. JSON files

print("\n7. JSON files")
employee = {
    "id": 101,
    "name": "Sudheer",
    "skills": ["Python", "SQL", "FastAPI"],
    "active": True,
}
with open(f"{folder}/employee.json", "w") as file:
    json.dump(employee, file, indent=4)      # dict -> JSON file

with open(f"{folder}/employee.json", "r") as file:
    data = json.load(file)                   # JSON file -> dict
print("Name  :", data["name"])
print("Skills:", ", ".join(data["skills"]))

8. Folders with os

print("\n8. Folder operations")
print("Files in folder:", os.listdir(folder))
print("Size of notes.txt:", os.path.getsize(f"{folder}/notes.txt"), "bytes")
print("Is notes.txt a file?", os.path.isfile(f"{folder}/notes.txt"))
print("Is folder a folder? ", os.path.isdir(folder))

9. Copy and move with shutil

print("\n9. shutil copy and move")
os.makedirs(f"{folder}/backup", exist_ok=True)
shutil.copy(f"{folder}/students.csv", f"{folder}/backup/students.csv")
shutil.move(f"{folder}/data_copy.bin", f"{folder}/backup/data_copy.bin")
print("Backup folder:", os.listdir(f"{folder}/backup"))

10. pathlib

print("\n10. pathlib")
path = Path(folder) / "pathlib_demo.txt"     # "/" joins paths
path.write_text("Written using pathlib\n")   # open + write + close in one line
print("Content :", path.read_text().strip())
print("Name    :", path.name)
print("Suffix  :", path.suffix)
print("Exists  :", path.exists())
print("All .txt files:", [p.name for p in Path(folder).glob("*.txt")])

11. Custom context manager

A class with __enter__ and __exit__ can be used with "with".

print("\n11. Custom context manager")

class MyFile:
    def __init__(self, name, mode):
        self.name = name
        self.mode = mode

    def __enter__(self):
        print("Opening", self.name)
        self.file = open(self.name, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_value, traceback):
        self.file.close()
        print("Closed", self.name)

with MyFile(f"{folder}/custom.txt", "w") as file:
    file.write("Written using my own context manager\n")