Code of the Day
BeginnerData and logic

Reading and writing files

Open, read, and write files safely using the with statement and handle missing files gracefully.

PythonBeginner9 min read
Recommended first
By the end of this lesson you will be able to:
  • Open files with open() in read, write, and append mode
  • Read all text at once and iterate line by line
  • Write text to a file
  • Use the with statement to close files automatically
  • Catch FileNotFoundError gracefully

Most real programs either read data from somewhere or record results somewhere. Python's built-in open() function handles both sides cleanly, and the with statement makes it nearly impossible to leave a file accidentally open.

Opening a file

open(path, mode) returns a file object. The mode controls what you can do with it:

ModeMeaning
'r'Read (default). File must exist.
'w'Write. Creates the file; overwrites if it exists.
'a'Append. Creates the file; adds to the end if it exists.
f = open("data.txt", "r")
content = f.read()
f.close()   # you must close — or use with (see below)

Forgetting close() leaks the file handle. The with statement fixes that.

The with statement — always use it

The with statement is a with open(...) guarantees the file is closed when the block exits, even if an exception is raised:

with open("data.txt", "r") as f:
    content = f.read()
# file is automatically closed here

There is essentially no reason to call open() outside a with block in production code. Make it a reflex.

Reading

Three patterns cover nearly every read case:

# 1. Read the entire file as one string
with open("notes.txt", "r") as f:
    text = f.read()

# 2. Read into a list of lines (newline included)
with open("notes.txt", "r") as f:
    lines = f.readlines()

# 3. Iterate line by line (memory-efficient for large files)
with open("notes.txt", "r") as f:
    for line in f:
        print(line.strip())   # strip() removes the trailing newline

Iterating directly over the file object (pattern 3) is the most Pythonic and handles large files without loading everything into memory.

Writing

'w' mode creates or replaces the file; 'a' mode appends to it:

with open("output.txt", "w") as f:
    f.write("Line one\n")
    f.write("Line two\n")

write() doesn't add a newline for you — include \n explicitly if you want each call to produce a separate line.

To write multiple lines from a list, f.writelines(lines) writes each item directly. As with write(), newlines are your responsibility — items are joined without a separator.

Handling FileNotFoundError

If you open a file for reading and it doesn't exist, Python raises a FileNotFoundError. Catch it specifically — the errors-and-exceptions lesson showed why catching the precise type matters:

try:
    with open("config.txt", "r") as f:
        config = f.read()
except FileNotFoundError:
    config = ""   # treat a missing file as empty config

A common variant: check whether a file exists before opening, using pathlib.Path.exists() — but the try/except approach is often cleaner and avoids a race condition between the check and the open.

A complete read-process-write example

This pattern — read lines, filter them, write results — appears constantly:

with open("data.txt", "r") as infile:
    lines = [line.strip() for line in infile if line.strip()]

with open("filtered.txt", "w") as outfile:
    for line in lines:
        outfile.write(line + "\n")

Opening a file with 'w' mode immediately truncates (empties) it, even before you write anything. If you need to read and then rewrite the same file, read it fully first, close it, then reopen with 'w'. Never open the same file for both reading and writing in two separate handles at once.

Where to go next

You can now persist data to disk and load it back. Next: working with strings in depth — the richer set of methods you'll use when processing that text.

Finished reading? Mark it complete to track your progress.

On this page