Skip to content

Python csv — Read & Write CSV

CSV (Comma-Separated Values) is the universal format for tabular data — spreadsheets, exports, datasets. The csv module reads and writes it correctly, handling quoting, commas inside fields, and newlines that naive string splitting would get wrong.

quickstart.py
import csv
import io
 
# Read rows from CSV text
text = "name,age\nAda,36\nBob,25"
reader = csv.reader(io.StringIO(text))
for row in reader:
    print(row)
# ['name', 'age']
# ['Ada', '36']
# ['Bob', '25']

Always open files with newline="" when using csv. This lets the module handle line endings itself and prevents blank rows on Windows.

ToolDirectionRow shape
csv.reader(file)ReadEach row is a list.
csv.writer(file)WriteYou pass lists.
csv.DictReader(file)ReadEach row is a dict keyed by header.
csv.DictWriter(file, fieldnames)WriteYou pass dicts.
writer.py
import csv
 
rows = [
    ["name", "city"],
    ["Ada", "London"],
    ["Bob", "New York, NY"],   # comma inside a field is handled automatically
]
 
with open("people.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["title", "year"])   # one row
    writer.writerows(rows)               # many rows

The module quotes fields that contain commas, quotes, or newlines, so "New York, NY" stays a single field.

reader.py
import csv
 
with open("people.csv", "r", newline="", encoding="utf-8") as f:
    reader = csv.reader(f)
    header = next(reader)        # pull the header row off first
    print("Header:", header)
    for row in reader:
        print(row)               # each row is a list of strings

Every value read from CSV is a string. Convert numbers yourself: int(row[1]), float(row[2]).

DictReader uses the first row as keys, so you access columns by name instead of position.

dictreader.py
import csv
import io
 
text = "name,age,city\nAda,36,London\nBob,25,NYC"
reader = csv.DictReader(io.StringIO(text))
for row in reader:
    print(row["name"], "is", row["age"])
# Ada is 36
# Bob is 25

DictWriter needs fieldnames; call writeheader() to emit the header row.

dictwriter.py
import csv
 
people = [
    {"name": "Ada", "age": 36},
    {"name": "Bob", "age": 25},
]
 
with open("out.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "age"])
    writer.writeheader()
    writer.writerows(people)

Not every “CSV” uses commas. Pass delimiter for TSV (tabs), semicolons, or pipes.

delimiters.py
import csv
import io
 
tsv = "name\tage\nAda\t36"
reader = csv.reader(io.StringIO(tsv), delimiter="\t")
print(list(reader))   # [['name', 'age'], ['Ada', '36']]
ParameterControls
delimiterField separator (default ,).
quotecharCharacter used to quote fields (default ").
quotingWhen to quote (csv.QUOTE_MINIMAL, QUOTE_ALL, …).
escapecharEscape character for special chars.
sketch Why splitting on commas is not parsing CSV p5.js
Three rows of real-world data: a name containing a comma, a note containing escaped quotes, and a field containing a newline. All three are legal CSV and all three defeat a split on commas. The manual version produces four rows with three, four, three and one field; the csv module produces the three rows with three fields each that the file actually describes. This is not an edge case you can decide to ignore -- any field a human typed can contain a comma.
  • Missing newline="" — causes blank rows between every record on Windows.
  • Forgetting values are stringsrow["age"] + 1 fails; convert with int(...) first.
  • DictWriter without writeheader() — produces a file with no header row.
  • Mismatched fieldnames — a dict key not in fieldnames raises ValueError.
  • The csv module handles quoting and embedded commas/newlines that manual splitting breaks.
  • Use reader/writer for lists, DictReader/DictWriter for dicts keyed by header.
  • Open files with newline="" and an explicit encoding.
  • Remember every read value is a string — convert numbers yourself.
  • Set delimiter for TSV and other separated formats.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading