Reading and writing files lets a script work with data that already exists, or that needs to persist after the script finishes. This page covers writing a text file, reading one back in several ways, and parsing its contents into usable values.

The Python documentation has a guide on Reading and Writing Files.

Writing a Text File

The built-in open() function opens a file, and with closes it automatically once the indented block finishes, even if an error happens inside it. Always prefer with over calling a file’s .close() method manually:

with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")
    f.write("Mercury-Redstone 3,187,15\n")
    f.write("Vostok 1,327,108\n")
    f.write("Freedom 7,187,15\n")

print("Wrote flights.txt")

Wrote flights.txt

The second argument to open(), "w", means write mode. It creates the file if it doesn’t exist, and overwrites it completely if it does.

Writing Several Lines at Once

.writelines() writes a list of strings in one call, instead of one .write() call per line:

rows = [
    "name,altitude_km,duration_min\n",
    "Mercury-Redstone 3,187,15\n",
    "Vostok 1,327,108\n",
]
with open("flights.txt", "w") as f:
    f.writelines(rows)   # note: writelines does NOT add '\n' for you

with open("flights.txt", "r") as f:
    print(f.read())

name,altitude_km,duration_min
Mercury-Redstone 3,187,15
Vostok 1,327,108


Unlike print(), .writelines() does not add a newline between entries. Each string in the list needs its own trailing \n, as shown above.

Reading a File

Reading the Entire File at Once

Opening a file with "r" (read mode) and calling .read() returns its entire contents as a single string:

with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")
    f.write("Mercury-Redstone 3,187,15\n")
    f.write("Vostok 1,327,108\n")

with open("flights.txt", "r") as f:
    contents = f.read()

print(contents)
print(type(contents))

name,altitude_km,duration_min
Mercury-Redstone 3,187,15
Vostok 1,327,108

<class 'str'>

Reading Line by Line

For a large file, reading it all at once can use a lot of memory. Looping over the open file object directly reads it one line at a time instead:

with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")
    f.write("Mercury-Redstone 3,187,15\n")
    f.write("Vostok 1,327,108\n")

with open("flights.txt", "r") as f:
    for line in f:
        print(repr(line))    # repr() shows the trailing '\n'

'name,altitude_km,duration_min\n'
'Mercury-Redstone 3,187,15\n'
'Vostok 1,327,108\n'

repr(line) is used here only to make the trailing \n at the end of each line visible. A plain print(line) would also work, just with an extra blank line between each one.

Parsing Fields with strip() and split()

Each line read from a file keeps its trailing newline, and a line of comma-separated data still needs to be broken into individual fields. .strip() removes the newline, and .split(",") breaks the result into a list of fields:

with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")
    f.write("Mercury-Redstone 3,187,15\n")
    f.write("Vostok 1,327,108\n")

with open("flights.txt", "r") as f:
    header = f.readline().strip().split(",")
    print(header)
    for line in f:
        fields = line.strip().split(",")
        print(fields)

['name', 'altitude_km', 'duration_min']
['Mercury-Redstone 3', '187', '15']
['Vostok 1', '327', '108']

Example: Building a List of Flight Records

Question

Given a CSV file with a header row (name,altitude_km,duration_min) followed by one flight per line, write a function that reads the file and returns a list of rows, where each row is a list [name, altitude_km, duration_min] with the numeric fields converted to float.

Solution

The function reads the header separately with .readline(), then loops over the remaining lines, splitting and converting each one before appending it to the result.

def read_flights(filename):
    """Read a CSV file into a list of rows, one list per flight."""
    flights = []
    with open(filename, "r") as f:
        header = f.readline().strip().split(",")
        for line in f:
            fields = line.strip().split(",")
            fields[1] = float(fields[1])
            fields[2] = float(fields[2])
            flights.append(fields)
    return header, flights


with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")
    f.write("Mercury-Redstone 3,187,15\n")
    f.write("Vostok 1,327,108\n")
    f.write("Freedom 7,187,15\n")

header, flights = read_flights("flights.txt")
print(header)
print(flights)
print(flights[0][0])
print(flights[0][1])

['name', 'altitude_km', 'duration_min']
[['Mercury-Redstone 3', 187.0, 15.0], ['Vostok 1', 327.0, 108.0], ['Freedom 7', 187.0, 15.0]]
Mercury-Redstone 3
187.0

Once you’ve learned about dictionaries, on the Dictionaries page, this same pattern can build a list of dictionaries instead of a list of lists, letting you access flights[0]["name"] by field name instead of position.

The csv Module

Manual .strip().split(",") parsing works for simple files, but breaks down on edge cases like a field that itself contains a comma inside quotes. The standard library’s csv module handles those cases, and csv.DictReader reads each row directly into a dict keyed by the header:

import csv

with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")
    f.write("Mercury-Redstone 3,187,15\n")
    f.write("Vostok 1,327,108\n")

with open("flights.txt", "r") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row)

{'name': 'Mercury-Redstone 3', 'altitude_km': '187', 'duration_min': '15'}
{'name': 'Vostok 1', 'altitude_km': '327', 'duration_min': '108'}

Each row behaves like a dictionary, so row["name"] reads that field by name. See the Dictionaries page for more on working with dicts.

Appending to a File

Opening a file with "a" (append mode) adds to the end of an existing file instead of overwriting it:

with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")
    f.write("Mercury-Redstone 3,187,15\n")

with open("flights.txt", "a") as f:
    f.write("Apollo 11,377349,11520\n")

with open("flights.txt", "r") as f:
    print(f.read())

name,altitude_km,duration_min
Mercury-Redstone 3,187,15
Apollo 11,377349,11520


File Modes

Mode Meaning
"r" Read (the default), raises an error if the file doesn’t exist
"w" Write (overwrites the file if it exists, creates it if not)
"a" Append (adds to the end, creates the file if not)
"r+" Read and write, without truncating

Checking Whether a File Exists

os.path.exists() checks whether a file (or folder) exists, without opening it:

import os

with open("flights.txt", "w") as f:
    f.write("name,altitude_km,duration_min\n")

print(os.path.exists("flights.txt"))
print(os.path.exists("does_not_exist.txt"))

True
False

This is useful for deciding whether to read an existing file or create a new one, before ever calling open().

Handling a Missing File

Trying to open a file that doesn’t exist in read mode raises a FileNotFoundError. Wrapping the open() call in a try-except block lets a script handle that case instead of crashing:

try:
    with open("does_not_exist.txt", "r") as f:
        f.read()
except FileNotFoundError:
    print("That file doesn't exist - check the filename and path.")

That file doesn't exist - check the filename and path.

Reading Questions

  1. What is the purpose of the with statement when working with files?
  2. What is the difference between "w" mode and "a" mode?
  3. What does .read() return when called on an open file?
  4. Why is looping over an open file object line by line sometimes preferable to .read()?
  5. What do .strip() and .split(",") do to a line of CSV data, and in what order would you typically apply them?
  6. What is the difference between print() and .writelines() when it comes to adding newlines?
  7. What does csv.DictReader return for each row, and how do you access a specific field from it?
  8. How would you check whether a file exists without opening it?
  9. What error does Python raise if you try to open a file that doesn’t exist in read mode, and how would you handle it?