Reading and writing files
Variables vanish when a program ends. A file stays: write today's lamp count into one, and tomorrow's program can read it back. Python opens a file with open(), and then you read from it or write to it.
On this page, each example works in its own make-believe folder. It starts with the files shown on the page (like lamps.txt below), and anything an example writes stays inside that example, so you can't break anything. On your own computer, the same code works with real files in the folder you run the program from.
Writing a file
open(name, "w") opens a file for writing, creating it if it isn't there. write adds text, and close finishes the file. Then open(name) opens it again for reading, and read gives back everything in it as one string:
1f = open("notes.txt", "w")2f.write("Lamps lit: 4\n")3f.write("Oil left: 2\n")4f.close()5 6f = open("notes.txt")7print(f.read(), end="")8f.close()
Lamps lit: 4 Oil left: 2
write does not start a new line by itself, so each line ends with \n. The file already ends with a new line, so end="" keeps print from adding another.
Forgetting close is an easy mistake, and if an error happens before it, the file may never be closed. A with block closes it for you when the block ends, even when an exception stops it. This is the way to open files from now on:
1with open("notes.txt", "w") as f:2 f.write("Lamps lit: 4\n")3 f.writelines(["Oil left: 2\n", "Bulbs: 7\n"])4print(f.closed)5 6with open("notes.txt") as f:7 print(f.read(), end="")
True Lamps lit: 4 Oil left: 2 Bulbs: 7
writelines writes every string in a list, one after another; like write, it adds no \n of its own.
Reading a file
Here is a file that is already in the folder. Every example on this page starts with it:
Elm Street Oak Lane Mill Road Harbor Way
read() gives the whole file as one string, and .splitlines() cuts it into lines. readline() reads just the next line, \n included, and readlines() gives the lines that are left as a list:
1with open("lamps.txt") as f:2 text = f.read()3print(text.splitlines())4 5with open("lamps.txt") as f:6 print(repr(f.readline()))7 print(f.readlines())
['Elm Street', 'Oak Lane', 'Mill Road', 'Harbor Way'] 'Elm Street\n' ['Oak Lane\n', 'Mill Road\n', 'Harbor Way\n']
repr() shows a string with its quotes and any \n, which helps when you want to see exactly what was read. Most often you will loop over the file itself, which gives one line at a time and works for files of any size:
1with open("lamps.txt") as f:2 for line in f:3 print("Light", line.strip())
Light Elm Street Light Oak Lane Light Mill Road Light Harbor Way
What is different without .strip()?
1with open("lamps.txt") as f:2 for line in f:3 print(line)
Each line still ends with its \n, and print adds a new line of its own, so every street is followed by an empty line. .strip() (or .rstrip("\n")) removes it.
Modes and appending
The second argument to open is the mode:
| Mode | Meaning | If the file exists | If it doesn't |
|---|---|---|---|
"r" | read (the default) | reads from the start | FileNotFoundError |
"w" | write | empties it first | creates it |
"a" | append | adds to the end | creates it |
Be careful with "w": opening a file that way wipes what was in it, before you write a single character. To add to a file, use "a":
1with open("lamps.txt", "a") as f:2 f.write("Quarry Road\n")3with open("lamps.txt") as f:4 print(len(f.readlines()), "streets")5 6with open("lamps.txt", "w") as f:7 f.write("Mine Gate\n")8with open("lamps.txt") as f:9 print(f.read(), end="")
5 streets Mine Gate
Tip: On your own computer, add encoding="utf-8" when a file has accents, emoji or non-English letters: open("lamps.txt", "a", encoding="utf-8"). Otherwise some systems, Windows in particular, read and write it with a different encoding and the text comes out garbled.
When the file isn't there
Reading a file that does not exist raises FileNotFoundError. Lesson 21's try handles it: here a missing save file just means starting fresh.
1try:2 with open("save.txt") as f:3 level = f.read()4except FileNotFoundError:5 level = "1"6print("Starting at level", level)7 8open("lamp.txt")
Starting at level 1
Traceback (most recent call last):
File "main.py", line 8, in <module>
open("lamp.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'lamp.txt'The last line has no try around it, so the misspelled name stops the program. The message names the file it looked for, which is the first thing to check: the spelling, and whether the file is in the folder the program runs from.
Numbers in files
A file holds text, so write only takes strings. f.write(12) is a TypeError. Turn numbers into text with str() on the way in, and back with int() or float() on the way out:
1readings = [12, 7, 30]2with open("meter.txt", "w") as f:3 for r in readings:4 f.write(str(r) + "\n")5 6total = 07with open("meter.txt") as f:8 for line in f:9 total += int(line)10print("Total:", total)
Total: 49
int() ignores the spaces and \n around a number, so int("12\n") is 12. An empty line would still be a ValueError.
CSV files
A CSV file (comma-separated values) is a table as plain text: one row per line, commas between the columns, and often the column names on the first line. Spreadsheets can open and save it. This one is in the folder too:
name,count ore,12 lamp oil,5 wick,40 glass,3
The csv module splits the lines for you. csv.reader gives each row as a list; csv.DictReader uses the first line as names and gives each row as a dictionary:
1import csv2 3with open("stock.csv", newline="") as f:4 for row in csv.reader(f):5 print(row)6 7with open("stock.csv", newline="") as f:8 for row in csv.DictReader(f):9 if int(row["count"]) < 10:10 print("Order more", row["name"])
['name', 'count'] ['ore', '12'] ['lamp oil', '5'] ['wick', '40'] ['glass', '3'] Order more lamp oil Order more glass
Every value comes back as a string, so int() is needed before comparing. Open CSV files with newline=""; the csv module handles line endings itself. Writing is the mirror image: csv.writer writes lists, csv.DictWriter writes dictionaries.
1import csv2 3orders = [{"name": "lamp oil", "count": 20}, {"name": "glass", "count": 10}]4with open("orders.csv", "w", newline="") as f:5 writer = csv.DictWriter(f, fieldnames=["name", "count"])6 writer.writeheader()7 writer.writerows(orders)8 9with open("orders.csv") as f:10 print(f.read(), end="")
name,count lamp oil,20 glass,10
JSON
JSON is a text format for nested data: dictionaries, lists, strings, numbers, True/False and None (written true, false and null in JSON). It is how many programs and websites exchange data. json.dump writes a value to a file and json.load reads it back, types and all (mostly: tuples come back as lists); dumps and loads (with an s, for string) do the same with a string.
1import json2 3town = {"name": "Pixel Town", "lamps": 4, "streets": ["Elm", "Oak"], "lit": True}4with open("town.json", "w") as f:5 json.dump(town, f)6 7with open("town.json") as f:8 loaded = json.load(f)9print(loaded["streets"])10print(loaded == town)11 12text = json.dumps(town)13print(text)
['Elm', 'Oak']
True
{"name": "Pixel Town", "lamps": 4, "streets": ["Elm", "Oak"], "lit": true}
Unlike a CSV file, the numbers come back as numbers and the list as a list. json.dumps(town, indent=2) spreads the text over several lines, which is easier to read.
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it. Each one starts with lamps.txt and stock.csv in the folder.
Count the streets
✓Read lamps.txt and print how many streets it lists.
4 streets
1count = 02# read lamps.txt here3print(count, "streets")
One more street
✓Append Quarry Road as a new line to lamps.txt, then read the file and print every street with its number.
1 Elm Street 2 Oak Lane 3 Mill Road 4 Harbor Way 5 Quarry Road
1with open("lamps.txt") as f:2 for line in f:3 print(line.strip())
Total stock
✓Use csv.DictReader on stock.csv to add up the count column.
Total: 60
1import csv2 3total = 04print("Total:", total)
What you learned
open(name, mode)opens a file;with open(...) as f:closes it for you, even after an error.read()gives everything,readline()one line,readlines()a list;for line in f:goes line by line, with\non each line."w"empties the file and writes,"a"adds to the end,"r"reads; a missing file raisesFileNotFoundError.- Files hold text:
str()numbers on the way in,int()orfloat()on the way out. csvreads and writes tables (reader,writer,DictReader,DictWriter);jsonsaves nested data (dump,load,dumps,loads).