๐Ÿ“„
Coding๐ŸŽ“ Ages 14-18Beginner 13 min read

Reading and Writing Files in Python

Learn to read and write text files in Python: open files safely with the with statement, use read, readlines and write, append data, handle paths and avoid common errors. Runnable code, a worked program and a quiz.

Key takeaways

  • open(path, mode) connects your program to a file; the mode chooses read, write or append
  • The with open(...) as f: block opens a file and closes it automatically when you are done
  • Reading: read() gets the whole file, readlines() gives a list of lines, or loop over the file directly
  • Writing with 'w' replaces the whole file; 'a' adds to the end without erasing what is there
  • write() does not add newlines for you, so include \n yourself when you want separate lines

Why programs need files

Everything a program stores in variables vanishes the moment it stops running. Close the window and your high score, your saved notes, your to-do list โ€” all gone. To keep information between runs, you have to save it somewhere permanent: a file on disk. Files are also how programs share data with each other, with you, and with the wider world.

In this lesson you'll learn to read text out of files and write text into them. If you're comfortable running a program and using variables, you're ready. Everything here works in a plain .py file or in any Python interpreter.

Opening a file

You connect your program to a file with the built-in open() function. It needs the filename and a mode that says what you intend to do:

f = open("notes.txt", "r")
content = f.read()
print(content)
f.close()

The three most common modes are:

ModeMeaningIf the file is missingEffect on existing content
"r"readerrorunchanged
"w"writecreates iterased first
"a"appendcreates itkept; new text added at end

Notice the last line, f.close(). An open file is a limited resource, and forgetting to close it can lose data or lock the file. The trouble is, if an error happens before close(), it never runs. There's a much safer pattern.

The with statement: the safe way

Almost all real Python file code uses a with block. It opens the file, gives it a name, and closes it automatically when the block ends โ€” even if something goes wrong inside:

with open("notes.txt", "r") as f:
    content = f.read()
    print(content)
# the file is now closed automatically

Read this as: "open notes.txt for reading, call it f, and run the indented block." When the indented code finishes, Python closes f for you. Use this form from now on; it's cleaner and far less error-prone than calling close() by hand.

Three ways to read

Once a file is open in read mode, you have choices about how to pull text out of it.

read() returns the entire file as one big string:

with open("poem.txt", "r") as f:
    whole = f.read()
print(whole)

readlines() returns a list, one string per line (each still ending in \n):

with open("poem.txt", "r") as f:
    lines = f.readlines()
print(len(lines), "lines")
print(lines[0])

Looping directly over the file is the most memory-friendly way, because it reads one line at a time. This is the idiom you'll use most:

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

line.strip() removes the trailing newline (and any stray spaces), so print doesn't add a second blank line. If you skip .strip(), you'll see double-spaced output, because print adds its own newline on top of the one already in the line.

Writing to a file

Opening in "w" mode lets you write. Be careful: it erases the file first. The write() method adds exactly the text you give it โ€” no automatic newlines:

with open("greeting.txt", "w") as f:
    f.write("Hello!\n")
    f.write("This is line two.\n")

Without the \n characters, both pieces of text would land on the same line. If you have a list of strings to save, writelines() writes them all in order (again, you supply the newlines):

items = ["apple\n", "banana\n", "cherry\n"]
with open("fruit.txt", "w") as f:
    f.writelines(items)

Appending instead of overwriting

When you want to add to a file without destroying what's already there โ€” a log, a journal, a running high-score list โ€” use "a":

with open("log.txt", "a") as f:
    f.write("Program started\n")

Run that program five times and you'll have five lines, not one. That's the whole difference between "w" (replace) and "a" (add).

Handling a missing file gracefully

Reading a file that doesn't exist raises a FileNotFoundError and stops your program. You can catch it and respond calmly instead of crashing:

try:
    with open("settings.txt", "r") as f:
        print(f.read())
except FileNotFoundError:
    print("No settings file yet โ€” using defaults.")

This try / except pattern is the polite way to deal with files that may or may not be there.

Worked example: a simple notes keeper

Let's combine reading, appending, and graceful error-handling into one complete program. It lets you add notes, then shows everything you've ever saved.

NOTES_FILE = "my_notes.txt"

def add_note(text):
    """Append one note (with a newline) to the notes file."""
    with open(NOTES_FILE, "a") as f:
        f.write(text + "\n")

def show_notes():
    """Print every saved note, numbered. Handle a missing file."""
    try:
        with open(NOTES_FILE, "r") as f:
            lines = f.readlines()
    except FileNotFoundError:
        print("You have no notes yet.")
        return

    if not lines:
        print("You have no notes yet.")
        return

    print("Your notes:")
    for number, line in enumerate(lines, start=1):
        print(f"{number}. {line.strip()}")

# Try the program
add_note("Buy milk")
add_note("Finish Python homework")
add_note("Call Grandma")
show_notes()

Walking through it:

  1. NOTES_FILE is a constant holding the filename in one place, so it's easy to change later.
  2. add_note opens in append mode and writes the text plus a \n, so each note sits on its own line and earlier notes are kept.
  3. show_notes reads all the lines into a list. If the file is missing, the except block prints a friendly message and returns early instead of crashing.
  4. enumerate(lines, start=1) gives both a counter and each line, so we can print numbered notes. .strip() removes the stored newline so the numbering looks tidy.

Run it once and three notes appear. Run it again and you'll see six โ€” proof that append mode keeps your data between runs.

Try it yourself

Turn the notes keeper into a tiny menu-driven app:

  • Add a loop that asks the user: type add to write a note, list to show all notes, or quit to stop. Use input and output to read their choice.
  • When they choose add, ask for the note text and call add_note.
  • When they choose list, call show_notes.
  • As a stretch, add a clear option that opens the file in "w" mode and writes nothing โ€” wiping it clean. Ask "Are you sure?" before you do, since "w" is unforgiving.

Once your data lives in files, the next step is organising it into rows and columns โ€” see working with CSV data in Python to store structured records instead of plain lines.

Quick quiz

Test yourself and earn XP

Which file mode opens a file for reading?

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

What does mode "w" do to a file that already contains text?

What does this print if scores.txt contains the line `42\n`? ``` with open("scores.txt") as f: line = f.readline() print(line.strip()) ```

Does write() automatically add a newline after the text?

FAQ

Both let you write to a file, but "w" (write) empties the file first, so it replaces everything. "a" (append) keeps the existing content and adds your new text at the end. Use "w" to create or overwrite a fresh file, and "a" to keep adding records like a log or a high-score list.

Python raises a FileNotFoundError and your program stops. You can prevent the crash by checking with os.path.exists() first, or by wrapping the open call in a try / except FileNotFoundError block so you can show a friendly message instead.