Structured Data Files
Intermediate · 18 min read · ▶ live playground · ✦ checkpoint
Plain text files are honest, but they make you do too much counting. If your program saves a score, a player, and a mood on each line, you want the file to remember that shape too. Structured data files are files with a predictable layout, so Python can turn file text back into lists, dictionaries, numbers, and strings with less guesswork.
Two everyday formats matter first: CSV for table-shaped data, and JSON for nested Python-shaped data. Both are still text files. The structure is the agreement about what the text means.
CSV: table-shaped files
CSV (comma-separated values) is a text format where each row is one line and each field is one value in that row. Picture a spreadsheet saved as plain text: each row is a line, and the commas mark the column boundaries.
Python's csv module handles the fussy parts, including a quoted field, a field wrapped in quotes so a comma inside it counts as data instead of a separator. Use it instead of splitting CSV lines by hand.
import csv
players = [
{"player": "Mina", "score": 42, "mood": "focused"},
{"player": "Ravi", "score": 35, "mood": "calm"},
{"player": "Noor", "score": 39, "mood": "curious"},
]
with open("scoreboard.csv", "w", newline="", encoding="utf-8") as file:
fieldnames = ["player", "score", "mood"]
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(players)
with open("scoreboard.csv", "r", newline="", encoding="utf-8") as file:
reader = csv.DictReader(file)
for row in reader:
print(row["player"], "scored", row["score"], "and feels", row["mood"])Mina scored 42 and feels focusedRavi scored 35 and feels calmNoor scored 39 and feels curious
DictWriter writes dictionaries as rows. fieldnames sets the column order and gives writeheader() the names for the header row, the first line that labels the columns. DictReader reads each row back as a dictionary, using that header row as the keys.
The newline="" argument looks odd, but it is the standard CSV habit. It lets the csv module control line endings itself, so your file does not gain surprise blank rows on some systems.
Reading CSV values carefully
CSV has one beginner surprise: every value comes back as text. The score 42 is written in the file as characters, not as an int object. If you want arithmetic, convert it after reading.
import csv
with open("orders.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=["item", "count"])
writer.writeheader()
writer.writerow({"item": "apples", "count": 3})
writer.writerow({"item": "tea", "count": 1})
writer.writerow({"item": "mangoes", "count": 2})
total = 0
with open("orders.csv", "r", newline="", encoding="utf-8") as file:
reader = csv.DictReader(file)
for row in reader:
count = int(row["count"])
total = total + count
print(row["item"], "->", count)
print("total items:", total)apples -> 3tea -> 1mangoes -> 2total items: 6
That conversion is not busywork. It is the boundary between storage and meaning. The file stores characters; your program decides which characters should become numbers, booleans, or something else.
Try it — save a small class list
Run this, then add one more learner dictionary. Keep the write and read in the same program so the browser page has the file when the read happens.
JSON: nested data files
JSON (JavaScript Object Notation) is a text format for nested data, data stored inside other data: dictionaries, lists, strings, numbers, booleans, and None-like empty values. If CSV is a spreadsheet, JSON is a labeled set of cubbies that can hold more cubbies inside.
Python's json module already appeared in the standard library tour for strings. For files, use json.dump() to write to an open file and json.load() to read from one.
import json
tracker = {
"owner": "Mina",
"items": [
{"name": "apples", "count": 3},
{"name": "tea", "count": 1},
{"name": "mangoes", "count": 2},
],
"packed": False,
"note": None,
}
with open("tracker.json", "w", encoding="utf-8") as file:
json.dump(tracker, file, indent=2)
with open("tracker.json", "r", encoding="utf-8") as file:
loaded = json.load(file)
print("owner:", loaded["owner"])
for item in loaded["items"]:
print(item["name"], "->", item["count"])
print("packed:", loaded["packed"])
print("note:", loaded["note"])owner: Minaapples -> 3tea -> 1mangoes -> 2packed: Falsenote: None
indent=2 makes the file easier for humans to read. Python still reads it the same way. Use JSON when your data naturally has nesting: a contact with several phone numbers, an expense tracker with many entries, or game settings with several named sections.
Use CSV when your data is a flat table. Use JSON when the shape is closer to the dictionaries and lists already in your program.
Text encoding and UTF-8
Encoding is the rule that turns text characters into bytes, small numbers computers use to store data, and back again when you read a file. UTF-8 is the modern encoding that can represent ordinary English plus names and words from many languages.
menu = [
"café: 2",
"jalapeño: 4",
"Mina: tea",
]
with open("menu.txt", "w", encoding="utf-8") as file:
for line in menu:
file.write(line + "\n")
with open("menu.txt", "r", encoding="utf-8") as file:
print(file.read(), end="")café: 2jalapeño: 4Mina: tea
For this course, use encoding="utf-8" whenever you open a text data file. It is explicit, readable, and it matches the web's default way of moving text around.
Checkpoint
Answer all three to mark this lesson complete
You can now save useful data, not just loose lines of text. The next step is making these programs sturdy when files are missing, input is odd, or a value cannot be converted.