Lesson 11 of 12

Python File Handling

Variables disappear when a program ends. To keep data, such as a to-do list, a sales report or a settings file, you save it to a file. Python file handling is straightforward: one built-in function, open(), handles reading and writing text files, and the standard library adds tools for paths, CSV spreadsheets and JSON data. Every example in this lesson creates the files it reads, so you can run each one in an empty folder.

Python file handling with open() and with

open(path, mode, encoding=...) returns a file object. Always use it inside a with block. When the block ends, Python closes the file for you, even if an error happened inside the block. An unclosed file can lose data that was not yet written to disk.

ModeMeaning
"r"Read (the default). Error if the file does not exist.
"w"Write. Creates the file, or empties it if it already exists.
"a"Append. Adds to the end; creates the file if needed.
"x"Create. Error if the file already exists, so nothing is overwritten by accident.
"rb", "wb"Binary read or write, for images, PDFs and other non-text files.

Always pass encoding="utf-8" for text files. Without it, Python may use your operating system’s default encoding, and characters such as ₹ or Tamil letters may break when the file moves to another computer.

with open("tasks.txt", "w", encoding="utf-8") as f:
    f.write("Pay electricity bill\n")
    f.write("Book train to Rameswaram\n")

with open("tasks.txt", "a", encoding="utf-8") as f:
    f.write("Buy flowers for Pongal\n")

with open("tasks.txt", encoding="utf-8") as f:
    content = f.read()
print(content)

with open("tasks.txt", encoding="utf-8") as f:
    for number, line in enumerate(f, start=1):
        print(number, line.strip())
Output
Pay electricity bill
Book train to Rameswaram
Buy flowers for Pongal

1 Pay electricity bill
2 Book train to Rameswaram
3 Buy flowers for Pongal

write() does not add a new line for you, so each string ends with \n. When reading, read() returns the whole file as one string, and looping over the file object gives one line at a time, each still ending in \n. That is why the loop calls strip(). It also explains the empty line in the output: the text from read() already ends with \n, and print() adds one more. Reading line by line also works for very large files, because Python does not load the whole file into memory.

Watch out: Opening an existing file with "w" wipes its contents immediately, before you write anything. If you want to add to a file, use "a"; if you must never overwrite, use "x".

Paths with pathlib

File paths look different on Windows (C:\Users\…) and on macOS or Linux (/home/…). The pathlib module hides those differences. A Path object joins parts with the / operator, tells you a file’s name, extension and whether it exists, and has short helpers for small files: write_text() and read_text() open, write or read, and close in one call.

from pathlib import Path

reports = Path("reports")
reports.mkdir(exist_ok=True)

for month in ["jan", "feb", "mar"]:
    (reports / f"sales-{month}.txt").write_text(f"Sales report for {month}\n", encoding="utf-8")

for file in sorted(reports.glob("*.txt")):
    print(file.name, "|", file.stem, "|", file.suffix)

feb = reports / "sales-feb.txt"
print(feb.exists())
print(feb.read_text(encoding="utf-8").strip())
print((reports / "sales-dec.txt").exists())
Output
sales-feb.txt | sales-feb | .txt
sales-jan.txt | sales-jan | .txt
sales-mar.txt | sales-mar | .txt
True
Sales report for feb
False

mkdir(exist_ok=True) creates the folder but does not complain if it is already there. glob("*.txt") finds matching files; its order depends on the operating system, so the example sorts the results to get a predictable list.

CSV files

CSV (comma-separated values) is the simplest way to exchange table data with Excel or Google Sheets. Do not split lines on commas yourself: a value such as "Mylapore, Chennai" contains a comma of its own. The built-in csv module handles quoting correctly. Open CSV files with newline="", as the documentation recommends, so line endings are handled properly on every system.

import csv

rows = [
    ["name", "area", "amount"],
    ["Ramesh", "Mylapore, Chennai", 1250],
    ["Fathima", "Gandhipuram, Coimbatore", 980],
    ["Joseph", "Fort Kochi", 1500],
]
with open("collections.csv", "w", newline="", encoding="utf-8") as f:
    csv.writer(f).writerows(rows)

total = 0
with open("collections.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        print(f"{row['name']:<8} {row['area']:<24} ₹{row['amount']}")
        total += int(row["amount"])
print("Total collected: ₹" + str(total))
Output
Ramesh   Mylapore, Chennai        ₹1250
Fathima  Gandhipuram, Coimbatore  ₹980
Joseph   Fort Kochi               ₹1500
Total collected: ₹3730

csv.DictReader uses the first row as headings and gives you each following row as a dictionary, so you can write row["amount"] instead of remembering column numbers. Every value comes back as a string, so convert numbers with int() or float() before doing maths.

JSON files

JSON is the format web APIs and many apps use for structured data. The json module converts Python dictionaries and lists to JSON text and back. json.dump() writes to a file and json.load() reads from one. Wrapping file access in try/except FileNotFoundError lets your program handle a missing file politely; you will learn more about this in Python Modules and Errors.

import json
from pathlib import Path

settings = {"theme": "dark", "language": "ta", "font_size": 16, "cities": ["Madurai", "Salem"]}
with open("settings.json", "w", encoding="utf-8") as f:
    json.dump(settings, f, indent=2)

with open("settings.json", encoding="utf-8") as f:
    loaded = json.load(f)
print(loaded["theme"], loaded["cities"][1])
print(Path("settings.json").read_text(encoding="utf-8").splitlines()[1])

try:
    with open("missing.json", encoding="utf-8") as f:
        data = json.load(f)
except FileNotFoundError:
    print("No saved file yet, using defaults.")
Output
dark Salem
  "theme": "dark",
No saved file yet, using defaults.

The middle line of output is the second line of the saved file. indent=2 spreads the JSON over several indented lines, so people can read and edit it; leave it out and everything is written on one compact line. json.load() gives back ordinary Python dictionaries and lists, so loaded["cities"][1] works exactly as in Python Dictionaries and Sets.

Tip: In Python file handling, relative paths such as "tasks.txt" are relative to the current working directory, which is the folder your terminal is in when you run the program, not necessarily the folder of the .py file. If a file seems to be missing, print Path.cwd() to see where Python is looking.

Further reading

Key takeaways

  • Open files in a with block so they are always closed, and pass encoding="utf-8".
  • Modes: "r" read, "w" write (empties the file), "a" append, "x" create only.
  • Loop over a file object to read line by line; write() needs explicit n.
  • pathlib.Path builds paths that work on every system and offers read_text() and write_text().
  • Use the csv module (with newline="") for spreadsheets and json for structured data.

Quick quiz

Check what you learned – pick an answer.

1. Which mode adds text to the end of an existing file?

2. Why is with open(...) as f: recommended?

3. What type are values read with csv.DictReader?

Practice exercise

Write a small expense logger: write one line per entry, such as Groceries,850, to expenses.txt, then read the file back and print each expense and the total.

Show a hint

Split each line with line.strip().split(","), and convert the amount with int().

Show the solution
entries = [("Groceries", 850), ("Auto fare", 120), ("Mobile recharge", 299)]

with open("expenses.txt", "w", encoding="utf-8") as f:
    for label, amount in entries:
        f.write(f"{label},{amount}n")

total = 0
with open("expenses.txt", encoding="utf-8") as f:
    for line in f:
        label, amount = line.strip().split(",")
        total += int(amount)
        print(f"{label}: ₹{amount}")

print(f"Total: ₹{total}")

Progress is saved in this browser only.