Working with CSV
CSV (comma-separated values) is a simple, text-based format for tabular data — every line is a row, and fields are separated by a delimiter (usually a comma). It's the lowest common denominator for exporting/importing spreadsheets, database tables, and logs between completely different systems. Python's built-in csv module handles the fiddly parts of the format for you — quoted fields containing commas, embedded newlines, escaping — that you'd otherwise get wrong by splitting on commas yourself.
Reading Rows with `csv.reader`
csv.reader() wraps an open file and yields each row as a plain list of strings. It knows nothing about headers — the first row it yields is whatever the first line of the file happens to be.
import csv
# contents of people.csv:
# name,age,city
# Ada,36,London
# Grace,85,New York
with open("people.csv", newline="") as f:
reader = csv.reader(f)
header = next(reader)
print("Header:", header)
for row in reader:
print(row)Header: ['name', 'age', 'city'] ['Ada', '36', 'London'] ['Grace', '85', 'New York']
Reading Rows as Dicts with `csv.DictReader`
csv.DictReader reads the first row as the header automatically and yields every subsequent row as an OrderedDict/dict keyed by those column names. This is almost always more convenient than indexing into a list by position.
import csv
with open("people.csv", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], "is", row["age"], "years old, from", row["city"])Ada is 36 years old, from London Grace is 85 years old, from New York
|
| |
|---|---|---|
Row type | A | A |
Header row | Returned like any other row — skip it yourself with | Consumed automatically to build the dict keys |
Typical use | Positional/simple data, or when you want full control | Named columns, more readable and refactor-safe code |
Writing Rows with `csv.writer` and `csv.DictWriter`
Writing mirrors reading. csv.writer takes an open file and writes rows from lists via writerow() (single row) or writerows() (many rows at once). csv.DictWriter does the same from dicts, but needs to know the column order up front via fieldnames=, and you must call writeheader() yourself if you want a header line.
import csv
rows = [
{"name": "Ada", "age": 36, "city": "London"},
{"name": "Grace", "age": 85, "city": "New York"},
]
with open("out.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
writer.writeheader()
writer.writerows(rows)Resulting out.csv:
name,age,city Ada,36,London Grace,85,New York
The `newline=""` Argument
Notice every example above opens files with newline="". The csv module manages its own line endings internally — on Windows, if you leave the default text-mode newline translation on, you end up with doubled \r\n sequences and phantom blank lines in the output. Passing newline="" when opening a file for csv.reader/csv.writer (in either direction) avoids this and is recommended on every platform, not just Windows.
Custom Delimiters
"CSV" is often used loosely to mean "delimited text", even when the delimiter isn't a comma. Semicolon-separated files are common in European locales (where the comma is a decimal separator), and tab-separated values (TSV) are common in exported data. Both reader/writer and DictReader/DictWriter accept a delimiter= argument.
import csv
# semicolon-separated
with open("euro_data.csv", newline="") as f:
for row in csv.reader(f, delimiter=";"):
print(row)
# tab-separated
with open("export.tsv", newline="") as f:
for row in csv.reader(f, delimiter=" "):
print(row)Worked Example: Filter and Transform
A common task: read records from a CSV, keep only the ones that match some condition, adjust a value, and write the result to a new CSV. Here we read a list of employees, keep only those in Engineering, give each a 10% raise, and write the result — including a new new_salary column — to raises.csv.
import csv
# employees.csv:
# name,department,salary
# Ada,Engineering,95000
# Grace,Engineering,102000
# Margaret,Sales,78000
with open("employees.csv", newline="") as infile:
reader = csv.DictReader(infile)
engineers = []
for row in reader:
if row["department"] == "Engineering":
row["new_salary"] = round(float(row["salary"]) * 1.10)
engineers.append(row)
fieldnames = ["name", "department", "salary", "new_salary"]
with open("raises.csv", "w", newline="") as outfile:
writer = csv.DictWriter(outfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(engineers)
print(f"Wrote {len(engineers)} rows to raises.csv")Wrote 2 rows to raises.csv
Resulting raises.csv:
name,department,salary,new_salary Ada,Engineering,95000,104500 Grace,Engineering,102000,112200
csv.reader/csv.writerwork with lists;csv.DictReader/csv.DictWriterwork with dicts keyed by header.DictWriterneedsfieldnames=up front and won't write a header unless you callwriteheader().Always open CSV files with
newline=""to avoid extra blank lines from newline translation.Use
delimiter=for semicolon- or tab-separated files.All fields read from a CSV are strings — convert types explicitly before doing math.