File Input/Output
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
- What is the purpose of the
withstatement when working with files? - What is the difference between
"w"mode and"a"mode? - What does
.read()return when called on an open file? - Why is looping over an open file object line by line sometimes preferable to
.read()? - What do
.strip()and.split(",")do to a line of CSV data, and in what order would you typically apply them? - What is the difference between
print()and.writelines()when it comes to adding newlines? - What does
csv.DictReaderreturn for each row, and how do you access a specific field from it? - How would you check whether a file exists without opening it?
- 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?