PythonWorking with CSV

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.

Python
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.

Python
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

csv.reader

csv.DictReader

Row type

A list of strings

A dict keyed by column name

Header row

Returned like any other row — skip it yourself with next()

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.

Python
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.

Python
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.

Python
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
Warning
Every value read from a CSV file is a plain `str`, even things that look like numbers. `row["salary"]` above is the string `"95000"`, not an `int` — you must explicitly convert with `int()`/`float()` before doing arithmetic, as shown with `float(row["salary"])`.
Note
For serious data analysis — filtering, joining, aggregating, or reshaping large CSV files — the standard tool in the Python ecosystem is the third-party `pandas` library and its `pandas.read_csv()` function, which loads a CSV straight into a powerful `DataFrame`. The built-in `csv` module is best suited to lightweight scripts, streaming line-by-line processing, and situations where you don't want an extra dependency.
  • csv.reader/csv.writer work with lists; csv.DictReader/csv.DictWriter work with dicts keyed by header.

  • DictWriter needs fieldnames= up front and won't write a header unless you call writeheader().

  • 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.