Skip to content

Python csv — Read & Write CSV

CSV (Comma-Separated Values) is the universal format for tabular data — spreadsheets, exports, datasets. The csvcsv 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']
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=""newline="" when using csvcsv. This lets the module handle line endings itself and prevents blank rows on Windows.

The four tools

ToolDirectionRow shape
csv.reader(file)csv.reader(file)ReadEach row is a list.
csv.writer(file)csv.writer(file)WriteYou pass lists.
csv.DictReader(file)csv.DictReader(file)ReadEach row is a dict keyed by header.
csv.DictWriter(file, fieldnames)csv.DictWriter(file, fieldnames)WriteYou pass dicts.

Writing with csv.writer

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
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""New York, NY" stays a single field.

Reading with csv.reader

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
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])int(row[1]), float(row[2])float(row[2]).

DictReader — rows as dictionaries

DictReaderDictReader 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
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 — write dictionaries

DictWriterDictWriter needs fieldnamesfieldnames; call writeheader()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)
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)

Custom delimiters and dialects

Not every “CSV” uses commas. Pass delimiterdelimiter 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']]
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
delimiterdelimiterField separator (default ,,).
quotecharquotecharCharacter used to quote fields (default "").
quotingquotingWhen to quote (csv.QUOTE_MINIMALcsv.QUOTE_MINIMAL, QUOTE_ALLQUOTE_ALL, …).
escapecharescapecharEscape character for special chars.

Common pitfalls

  • Missing newline=""newline="" — causes blank rows between every record on Windows.
  • Forgetting values are stringsrow["age"] + 1row["age"] + 1 fails; convert with int(...)int(...) first.
  • DictWriterDictWriter without writeheader()writeheader() — produces a file with no header row.
  • Mismatched fieldnamesfieldnames — a dict key not in fieldnamesfieldnames raises ValueErrorValueError.

Practice Exercises

Exercise 1 – Parse CSV text

Exercise 2 – Sum a numeric column

Exercise 3 – Write rows to CSV text

Summary

  • The csvcsv module handles quoting and embedded commas/newlines that manual splitting breaks.
  • Use readerreader/writerwriter for lists, DictReaderDictReader/DictWriterDictWriter for dicts keyed by header.
  • Open files with newline=""newline="" and an explicit encodingencoding.
  • Remember every read value is a string — convert numbers yourself.
  • Set delimiterdelimiter for TSV and other separated formats.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did