The csv module handles CSV files correctly—quoting, escaping, and edge cases that break naive string splitting.

Reading CSV

import csv
 
with open('data.csv', 'r', newline='') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)  # List of values
 
# data.csv:
# name,age,city
# Alice,30,New York
# Bob,25,"Los Angeles"
 
# Output:
# ['name', 'age', 'city']
# ['Alice', '30', 'New York']
# ['Bob', '25', 'Los Angeles']

Writing CSV

import csv
 
rows = [
    ['name', 'age', 'city'],
    ['Alice', 30, 'New York'],
    ['Bob', 25, 'Los Angeles'],
]
 
with open('output.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerows(rows)
    
    # Or one at a time
    writer.writerow(['Charlie', 35, 'Chicago'])

Always use newline='' to prevent blank line issues on Windows.

DictReader: Named Columns

import csv
 
with open('data.csv', 'r', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row['name'], row['age'])
 
# First row becomes field names by default
# row is a dict: {'name': 'Alice', 'age': '30', 'city': 'New York'}

Custom field names:

reader = csv.DictReader(f, fieldnames=['first', 'second', 'third'])

DictWriter: Write from Dicts

import csv
 
users = [
    {'name': 'Alice', 'age': 30, 'city': 'New York'},
    {'name': 'Bob', 'age': 25, 'city': 'Los Angeles'},
]
 
with open('output.csv', 'w', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=['name', 'age', 'city'])
    writer.writeheader()  # Write column names
    writer.writerows(users)

Custom Delimiters

import csv
 
# Tab-separated (TSV)
with open('data.tsv', 'r', newline='') as f:
    reader = csv.reader(f, delimiter='\t')
 
# Semicolon-separated (European Excel)
with open('data.csv', 'r', newline='') as f:
    reader = csv.reader(f, delimiter=';')
 
# Pipe-separated
reader = csv.reader(f, delimiter='|')

Quoting Options

import csv
 
# Quote all fields
writer = csv.writer(f, quoting=csv.QUOTE_ALL)
 
# Quote only non-numeric
writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC)
 
# Minimal quoting (default)
writer = csv.writer(f, quoting=csv.QUOTE_MINIMAL)
 
# No quoting
writer = csv.writer(f, quoting=csv.QUOTE_NONE, escapechar='\\')

Handling Edge Cases

Fields with Commas

# Automatically quoted
data = [['name', 'address'], ['Alice', '123 Main St, Apt 4']]
writer.writerows(data)
# Output: name,address
#         Alice,"123 Main St, Apt 4"

Fields with Quotes

data = [['quote', 'value'], ['He said "hello"', 'test']]
writer.writerows(data)
# Output: quote,value
#         "He said ""hello""",test

Newlines in Fields

data = [['text'], ['Line 1\nLine 2']]
writer.writerows(data)
# Output: text
#         "Line 1
#         Line 2"

Reading from Strings

import csv
import io
 
csv_string = """name,age
Alice,30
Bob,25"""
 
reader = csv.reader(io.StringIO(csv_string))
for row in reader:
    print(row)

Custom Dialects

import csv
 
# Register a custom dialect
csv.register_dialect('pipes', delimiter='|', quoting=csv.QUOTE_MINIMAL)
 
with open('data.txt', 'r') as f:
    reader = csv.reader(f, dialect='pipes')
 
# Built-in dialects
csv.reader(f, dialect='excel')     # Standard CSV
csv.reader(f, dialect='excel-tab') # TSV
csv.reader(f, dialect='unix')      # Unix-style

Practical Examples

Convert Types

import csv
 
with open('numbers.csv', 'r', newline='') as f:
    reader = csv.reader(f)
    next(reader)  # Skip header
    
    for row in reader:
        name, age, salary = row
        age = int(age)
        salary = float(salary)
        print(f"{name}: {age} years, ${salary:,.2f}")

Filter and Transform

import csv
 
with open('input.csv', 'r', newline='') as infile:
    with open('output.csv', 'w', newline='') as outfile:
        reader = csv.DictReader(infile)
        writer = csv.DictWriter(outfile, fieldnames=['name', 'age'])
        
        writer.writeheader()
        for row in reader:
            if int(row['age']) >= 18:
                writer.writerow({
                    'name': row['name'].upper(),
                    'age': row['age']
                })

Append to CSV

import csv
 
new_row = ['Charlie', 35, 'Chicago']
 
with open('data.csv', 'a', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(new_row)

CSV to JSON

import csv
import json
 
with open('data.csv', 'r', newline='') as f:
    reader = csv.DictReader(f)
    data = list(reader)
 
with open('data.json', 'w') as f:
    json.dump(data, f, indent=2)

Quick Reference

import csv
 
# Read
reader = csv.reader(file)
reader = csv.DictReader(file)
 
# Write
writer = csv.writer(file)
writer = csv.DictWriter(file, fieldnames=['a', 'b'])
writer.writeheader()  # DictWriter only
writer.writerow(row)
writer.writerows(rows)
 
# Options
csv.reader(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
Quoting ConstantBehavior
QUOTE_MINIMALQuote only when needed
QUOTE_ALLQuote everything
QUOTE_NONNUMERICQuote non-numbers
QUOTE_NONENever quote (use escapechar)

Don't parse CSV by hand with split(','). The csv module handles all the edge cases you'll inevitably hit.

React to this post: