Work with CSV Files in Python

Learn how to use Python's csv module to read and write CSV files, handle headers, choose delimiters, and manage quoting.

7 min read

The csv module in the Python standard library reads and writes CSV (Comma-Separated Values) files, a widely used format for spreadsheets, data exports, and tabular data exchange. You can read rows as lists or as dictionaries keyed by column headers, and you can write data from lists or dictionaries with equal ease.

pythonpython
import csv
 
with open("students.csv", newline="") as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)

The loop prints one list per row, starting with the header row itself, because csv.reader does not treat the first line as special in any way. Each value comes back as plain text, even the numeric-looking score column:

texttext
['name', 'score', 'grade']
['Maya', '92', 'A']
['Devin', '85', 'B']
['Priya', '78', 'C']

csv.reader(file) returns an iterator over the rows. Each row is a list of strings. The first row typically contains column headers, but csv.reader treats it the same as any other row.

Always open CSV files with newline="". This prevents the csv module from adding extra carriage returns on Windows and is harmless on other platforms.

Reading CSV as dictionaries

Accessing values by numeric index gets confusing once a file has more than a few columns. csv.DictReader solves this by reading each row as a dict (an OrderedDict before Python 3.8) keyed by the header row, so you can look up a value by its column name instead.

pythonpython
import csv
 
with open("students.csv", newline="") as file:
    reader = csv.DictReader(file)
    for row in reader:
        print(f"{row['name']} scored {row['score']}")

Each row now behaves like a dictionary, so the loop reads values by column name instead of by position, which makes the code easier to follow when a CSV file has many columns:

texttext
Maya scored 92
Devin scored 85
Priya scored 78

DictReader uses the first row as column names automatically. Each subsequent row becomes a dictionary where you access values by column name instead of numeric index.

If your CSV has no header row, pass the fieldnames yourself:

pythonpython
reader = csv.DictReader(file, fieldnames=["name", "score", "grade"])

Writing CSV from lists

csv.writer writes rows from lists or tuples. Give it a file opened for writing, then pass either a single row to writerow or a full collection of rows to writerows, and the module takes care of adding commas and line endings correctly.

pythonpython
import csv
 
rows = [
    ["name", "score", "grade"],
    ["Maya", "92", "A"],
    ["Devin", "85", "B"],
]
 
with open("output.csv", "w", newline="") as file:
    writer = csv.writer(file)
    writer.writerows(rows)

writer.writerows(rows) writes all rows at once. For writing one row at a time, use writer.writerow(row).

Writing CSV from dictionaries

csv.DictWriter writes rows from dictionaries. You must provide the fieldnames so the writer knows which keys to include and in what order.

pythonpython
import csv
 
fieldnames = ["name", "score", "grade"]
rows = [
    {"name": "Maya", "score": 92, "grade": "A"},
    {"name": "Devin", "score": 85, "grade": "B"},
]
 
with open("grades.csv", "w", newline="") as file:
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(rows)

writer.writeheader() writes the column names as the first row. writer.writerows(rows) writes the data rows. The fieldnames list determines both the header and the order of values in each output row.

Handling custom delimiters

Not all CSV files use commas. Tab-separated values (TSV) and semicolon-separated files are also common. Use the delimiter parameter.

pythonpython
import csv
 
with open("data.tsv", newline="") as file:
    reader = csv.reader(file, delimiter="\t")
    for row in reader:
        print(row)

The same delimiter argument works for semicolon-separated files, which are common in European CSV exports because some regional settings already use the comma as a decimal separator:

pythonpython
reader = csv.reader(file, delimiter=";")

The delimiter parameter works the same way with DictReader, writer, and DictWriter.

Quoting and special characters

When a field contains the delimiter character, a newline, or a quote character, the csv module automatically wraps it in quotes. You can control this behavior with the quoting parameter.

pythonpython
import csv
 
data = [
    ["name", "description"],
    ["Maya", "Likes Python, hiking, and photography"],
    ["Devin", 'Says "hello" in 5 languages'],
]
 
with open("quoted.csv", "w", newline="") as file:
    writer = csv.writer(file, quoting=csv.QUOTE_MINIMAL)
    writer.writerows(data)

QUOTE_MINIMAL (the default) only quotes fields that contain special characters. Other options:

Quoting constantBehavior
csv.QUOTE_MINIMALQuote only when necessary (default)
csv.QUOTE_ALLQuote every field
csv.QUOTE_NONNUMERICQuote all non-numeric fields
csv.QUOTE_NONENever quote; raises error on special characters

For most cases, the default QUOTE_MINIMAL is correct and produces clean output.

Reading from a string

Use io.StringIO to treat a string as a file-like object for the csv module.

pythonpython
import csv
from io import StringIO
 
data = "name,score,grade\nMaya,92,A\nDevin,85,B\n"
 
reader = csv.DictReader(StringIO(data))
for row in reader:
    print(row)

StringIO wraps the string so csv.DictReader can read it exactly as if it came from an open file, splitting on newlines and turning each data row into a dictionary keyed by the header:

texttext
{'name': 'Maya', 'score': '92', 'grade': 'A'}
{'name': 'Devin', 'score': '85', 'grade': 'B'}

This is useful for testing CSV parsing logic or handling CSV data received from an API response. If the API instead returns nested or non-tabular data, reading and writing JSON files is usually a better fit than CSV.

Practical example: filtering and transforming CSV data

Combine reading and writing to transform a CSV file.

pythonpython
import csv
 
with open("employees.csv", newline="") as infile, \
     open("engineers.csv", "w", newline="") as outfile:
 
    reader = csv.DictReader(infile)
    writer = csv.DictWriter(outfile, fieldnames=reader.fieldnames)
    writer.writeheader()
 
    for row in reader:
        if row.get("department") == "Engineering":
            writer.writerow(row)

This script reads employees.csv, keeps only rows where the department is "Engineering", and writes the result to engineers.csv. Both files are opened together with the with statement, and both are properly closed when the block ends.

Common mistakes

Forgetting newline="". On Windows, opening a CSV file without newline="" inserts an extra blank line between every data row. This setting is required for correct CSV handling and is safe on all platforms.

Treating all values as strings. The csv module reads every field as a string. If you need numbers, convert them manually:

pythonpython
row["score"] = int(row["score"])

Relying on column order with DictReader. DictReader returns dictionaries, so column order in the source file technically does not matter for access. But if your code assumes a specific order (like list(row.values())), a reordered input file will break it. Always access by column name.

Using csv.writer without a header. If you write data without a header row, readers cannot use DictReader later. Include a header row unless the file format explicitly omits it.

Rune AI

Rune AI

Key Insights

  • Use csv.reader(file) to read rows as lists and csv.DictReader(file) to read rows as dictionaries keyed by header names.
  • Use csv.writer(file) to write rows from lists and csv.DictWriter(file, fieldnames=...) to write from dictionaries.
  • Always open CSV files with newline='' to avoid extra blank lines on Windows.
  • Handle custom delimiters with the delimiter parameter.
  • CSV files are read row by row, so they work efficiently with large files.
  • Use the quoting parameter to control how special characters and commas inside fields are handled.
RunePowered by Rune AI

Frequently Asked Questions

Should I use csv.reader or csv.DictReader?

Use `csv.DictReader` when your CSV has a header row and you want to access columns by name. Use `csv.reader` when you need row-by-index access, when there are no headers, or when speed is critical and you do not need named columns.

How do I handle CSV files with a different delimiter?

Pass the `delimiter` argument to `csv.reader` or `csv.DictReader`. For tab-separated files, use `delimiter='\t'`. For semicolon-separated files common in European locales, use `delimiter=';'`.

Can the csv module handle large files?

Yes. The csv module reads one row at a time, so it does not load the entire file into memory. You can process files that are gigabytes in size as long as you iterate row by row instead of collecting all rows into a list.

Conclusion

The csv module is the standard tool for tabular data in Python. Use csv.reader for row-by-index access and csv.DictReader when you want to work with named columns. Always open CSV files with newline='' to prevent extra blank lines, and use the with statement to ensure files are properly closed.