Unit 1 • Lesson 4

Reading a CSV File

Overview

The csv module turns a file of text into rows you can work with.

Beginner 20–25 min

What You Will Learn in This Lesson

By the end of this lesson, you will be able to:

  • Say why splitting each line on commas breaks on real files.
  • Read a file with csv.reader inside a with block.
  • Read named records with csv.DictReader.
  • Remember that every value read from a file is a string.

Why This Matters

Almost all data you analyse arrives as a file, and CSV is the most common format there is. Reading it correctly is the “get the data” step for real — and the two traps in it, quoted commas and everything-is-text, are the same two traps pandas.read_csv handles for you in unit 5. Seeing them by hand first is what makes its options make sense.

Step 1: Why not just split on commas

A CSV file looks like lines of values separated by commas, so line.split(",") looks like it should work. It works until the first real file.

1

A comma inside a value

A name like "Lovelace, Ada" is one field. CSV protects it with quotes; split does not know about quotes and cuts the row in the wrong place, shifting every column after it.

line = '"Lovelace, Ada",92'

print(line.split(","))   # three pieces -- wrong
2

The csv module knows the rules

csv.reader understands quoting, escaped quotes and the line endings different systems write. Here it reads from an in-memory string, so the example runs without a file; on a real file the loop is identical.

import csv
import io

text = 'name,score\n"Lovelace, Ada",92\nGrace,88\n'
for row in csv.reader(io.StringIO(text)):
    print(row)

Mini Practice #1: Split versus csv

Try It Yourself

Print what split makes of each line, then what csv.reader makes of the same text. Count the fields in each.

Press Run to see output

Step 2: Opening the file properly

A file has to be opened and closed. The with statement does both, including when something goes wrong halfway through.

1

with open(...) as f

The block reads the file; leaving the block closes it. The example writes a small file first so there is something to read.

import csv

with open("scores.csv", "w") as f:
    f.write("name,score\nAda,92\nGrace,88\n")

with open("scores.csv") as f:
    for row in csv.reader(f):
        print(row)
2

The header is just the first row

csv.reader does not treat the header specially. next(reader) takes it off the front, so the loop sees only data.

import csv

with open("scores.csv", "w") as f:
    f.write("name,score\nAda,92\nGrace,88\n")

with open("scores.csv") as f:
    reader = csv.reader(f)
    header = next(reader)
    print("columns:", header)
    for row in reader:
        print(row)

Mini Practice #2: Skip the header

Try It Yourself

Write the file, then read it with csv.reader. Take the header off with next and print only the names.

Press Run to see output

Step 3: DictReader gives you named rows

The last lesson argued for records keyed by column name. csv.DictReader builds exactly those, using the header line for the keys.

1

One dictionary per row

No next, no zip: the header becomes the keys automatically, and row["score"] works straight away.

import csv

with open("scores.csv", "w") as f:
    f.write("name,score\nAda,92\nGrace,88\n")

with open("scores.csv") as f:
    for row in csv.DictReader(f):
        print(row["name"], row["score"])
2

Everything arrives as text

A file is text, so row["score"] is "92", not 92. Adding two of them concatenates. Convert on the way in, with int or float, before any arithmetic.

import csv

with open("scores.csv", "w") as f:
    f.write("name,score\nAda,92\nGrace,88\n")

with open("scores.csv") as f:
    rows = list(csv.DictReader(f))

print(rows[0]["score"] + rows[1]["score"])            # "9288"
print(int(rows[0]["score"]) + int(rows[1]["score"]))  # 180

Key idea: The exercises below use a supplied scores.csv. Pressing Run puts that file in place for you, and Check swaps in different files to make sure your code reads it rather than reciting it.

Predict, then check: Parse first, convert second

The header is deliberately in a different order, and the name contains a comma. Predict the two printed values.

import csv
import io

text = 'score,name\n7,"Hopper, Grace"\n'
row = next(csv.DictReader(io.StringIO(text)))
print(row["name"])
print(int(row["score"]) + 1)
Show the output and explanation
Hopper, Grace
8

DictReader uses the header names, so changing the column order does not change the meaning. It handles the quoted comma as part of the name. The score is still text: int converts it before addition. Opening a real CSV with newline="" lets the csv module handle line endings itself.

Try one change: Change the score to 12 and reverse the header and values together. The name should still print correctly.

Go deeper: Parse the file before interpreting its values

CSV is a file format with quoting rules, not just text separated at every comma. DictReader uses the header row as field names and returns string values. Parsing establishes the rows; conversion then gives selected fields the numeric types needed for analysis.

Trace the example

Predict the output before opening the explanation.

import csv
from io import StringIO

text = 'name,score\n"Lee, Jo",8\nAda,10\n'
rows = list(csv.DictReader(StringIO(text)))
print(rows[0]["name"])
print(sum(int(row["score"]) for row in rows))
Show the output and trace
Lee, Jo
18

The comma inside the quoted name belongs to that one field. DictReader keeps Lee, Jo together. The score values start as "8" and "10", then int converts them before summing. StringIO lets this small example behave like a text file without requiring an upload.

A mistake to watch for

line.split(",") would split the quoted name incorrectly. Also, a DictReader is consumed as you iterate; a second pass over the same reader will not start again. Save rows in a list for repeated access, or reopen the file for another pass.

Transfer challenge

Add a third CSV row for "Pat, Sam" with a score of 12. Print all three names and the new total. Keep the name quoted inside the CSV text.

Change the example below to solve the challenge. Predict the result, run your code, and compare. This practice is ungraded.

Press Run to see output
Compare with a worked solution
import csv
from io import StringIO
text = 'name,score\n"Lee, Jo",8\nAda,10\n"Pat, Sam",12\n'
rows = list(csv.DictReader(StringIO(text)))
print([row["name"] for row in rows])
print(sum(int(row["score"]) for row in rows))

Run both versions. Explain any difference in their output, then change one input and predict the new result.

Step 4: When to Use It

Any comma-separated file

Spreadsheet exports, logs and downloads. csv handles quoting and line endings that split() gets wrong.

with open(path) as f:
    rows = list(csv.DictReader(f))

Files with a header row

DictReader turns the header into keys, so the rest of the code reads fields by name.

When Not to Use It

Do not split lines on commas yourself, even for a file that looks simple today. The first quoted name with a comma in it shifts every column after it, with no error.

End-of-Lesson Exercises

Exercise 1: Print every row

A file scores.csv is supplied, with a name and a score column. Print each name and score on its own line, separated by a space.

DictReader reads the header for you, so row["name"] and row["score"] work straight away. Typing the three lines out passes the first check and fails the second.

Press Run to try it, then Check my work to mark it.

Exercise 2: Total a column from a file

Write total_score(path) that reads the CSV at path (columns name and score) and returns the sum of the scores as a number. Scores are integer text. A quoted name may contain a comma, columns may be reordered, and a header-only file should return 0. Use the path passed into the function; return the number rather than printing it.

Loop csv.DictReader(f) and add int(row["score"]) each time. Splitting lines on commas breaks on "Lovelace, Ada", and adding the strings without int() joins them instead of summing.

Press Run to try it, then Check my work to mark it.