Files and errorsChapter 80 of 114
Read Files
Read a whole file, a line at a time, or lazily for a big one.
Setting up
Every example below reads this file:
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada,1815\nGrace,1906\nKatherine,1918\n")
print("written")Output
written
read gives you the whole thing
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada,1815\nGrace,1906\n")
with open("people.txt", encoding="utf-8") as f:
content = f.read()
print(repr(content))
print(len(content))Output
'Ada,1815\nGrace,1906\n' 20
One string, newlines included. Fine for a configuration file, bad for a five-gigabyte log.
readlines gives you a list
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada,1815\nGrace,1906\n")
with open("people.txt", encoding="utf-8") as f:
lines = f.readlines()
print(lines)Output
['Ada,1815\n', 'Grace,1906\n']
The newline stays on the end of each line. That trips people up, and strip() is the usual answer.
Looping is the right default
A file object is iterable, and gives one line at a time without loading the rest:
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada,1815\nGrace,1906\nKatherine,1918\n")
with open("people.txt", encoding="utf-8") as f:
for line in f:
name, year = line.strip().split(",")
print(f"{name} was born in {year}")Output
Ada was born in 1815 Grace was born in 1906 Katherine was born in 1918
This works on a file far larger than memory, because only one line is held at a time. Make it your default.
Add enumerate when you want line numbers:
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada\nGrace\n")
with open("people.txt", encoding="utf-8") as f:
for number, line in enumerate(f, start=1):
print(number, line.strip())Output
1 Ada 2 Grace
Stripping the newline
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada\nGrace\n")
with open("people.txt", encoding="utf-8") as f:
names = [line.strip() for line in f]
print(names)Output
['Ada', 'Grace']
splitlines() does the same job on a string you already have, and drops the newlines for you:
text = "Ada\nGrace\n"
print(text.splitlines())
print(text.split("\n"))Output
['Ada', 'Grace'] ['Ada', 'Grace', '']
Note the trailing empty string from split("\n"). splitlines() is almost always what you want.
Reading a bit at a time
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada,1815\n")
with open("people.txt", encoding="utf-8") as f:
print(repr(f.read(3)))
print(repr(f.readline()))Output
'Ada' ',1815\n'
The file remembers its position, so the second call carries on where the first stopped.
Reading is one-way
Once you have read to the end, there is nothing left:
with open("people.txt", "w", encoding="utf-8") as f:
f.write("Ada\n")
with open("people.txt", encoding="utf-8") as f:
print(repr(f.read()))
print(repr(f.read()))
f.seek(0)
print(repr(f.read()))Output
'Ada\n' '' 'Ada\n'
seek(0) rewinds. If you find yourself doing that a lot, read into a list once instead.
CSV files
Do not split on commas by hand. A real CSV can have commas inside quoted fields, and csv handles it:
import csv
with open("people.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["name", "note"])
writer.writerow(["Ada", "maths, and computing"])
with open("people.csv", encoding="utf-8", newline="") as f:
for row in csv.reader(f):
print(row)Output
['name', 'note'] ['Ada', 'maths, and computing']
Splitting that second row on commas would have given three fields instead of two.
Test yourself
2 questionsWhat is the best default way to read a large file?
Show the answer
Loop over the file object, one line at a time — Only one line is held at a time, so it works on files larger than memory.
What does readlines() leave on the end of each line?
Show the answer
The newline character — strip() is the usual answer, or splitlines() when you already have the text.
Write Files
Create, overwrite and append, without losing what was there.