How to Write CSV Values With Commas, Quotes and Newlines in Python

A CSV built with ','.join(row) breaks as soon as a value contains a comma, a double quote or a line break - Smith, Bob becomes two columns. CSV has quoting rules for these cases, and Python's built-in csv module applies them for you. Write rows with csv.writer into a file or an io.StringIO, and every cell comes back exactly as it went in.

Start this project - freeLive cloud workspace in your browser · lands on your portfolio
Backend Engineerpythoncsvescaping

The bug

Here is a hand-written CSV export:

def export_csv(rows: list[list[str]]) -> str:
    out = ""
    for row in rows:
        out += ",".join(row) + "\n"
    return out

It works for clean data. Then a customer name contains a comma:

import csv, io
text = export_csv([["1", "Smith, Bob"]])
print(list(csv.reader(io.StringIO(text))))
# [['1', 'Smith', ' Bob']]   - 3 cells instead of 2

Any program that reads this file - Excel, pandas, a database import - sees an extra column. A double quote or a multi-line address breaks the row in other ways.

How CSV quoting works

The common CSV format (described in RFC 4180) has three rules:

  1. A cell that contains a comma, a double quote or a line break is wrapped in double quotes.
  2. A double quote inside a quoted cell is written twice: " becomes "".
  3. A line break inside a quoted cell is part of the value, not the end of the row.

So these values:

Value Written as
Smith, Bob "Smith, Bob"
O"Reilly "O""Reilly"
12 Main St + line break + Apt 4 "12 Main St (line break) Apt 4"

You could write these rules yourself, but it is easy to miss a case. The standard library already does it.

The fix: csv.writer

import csv
import io

def export_csv(rows: list[list[str]]) -> str:
    buf = io.StringIO()
    csv.writer(buf).writerows(rows)
    return buf.getvalue()

Check it with the same round-trip test:

rows = [
    ["id", "name", "address"],
    ["1", "Smith, Bob", "12 Main St"],
    ["2", 'O"Reilly', "12 Main St\nApt 4"],
]
text = export_csv(rows)
assert list(csv.reader(io.StringIO(text))) == rows

The writer quotes only the cells that need it, doubles inner quotes, and keeps line breaks inside quoted cells. The reader turns the text back into the original values.

Writing to a file

When you write to a file, open it with newline="":

with open("customers.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["id", "name"])
    writer.writerows(data_rows)

csv.writer ends each row with \r\n by default. Without newline="", Python may change the line endings again on some systems, and you get empty lines between rows when the file is opened in Excel.

Writing dictionaries with DictWriter

If your rows are dicts (for example from a database cursor), csv.DictWriter maps keys to columns:

fields = ["id", "name", "email"]
writer = csv.DictWriter(buf, fieldnames=fields, extrasaction="ignore")
writer.writeheader()
writer.writerows(customers)   # list of dicts

extrasaction="ignore" drops keys that are not in fields instead of raising an error.

Returning CSV from a Flask endpoint

from flask import Response

@app.get("/export")
def export():
    text = export_csv(load_rows())
    return Response(
        text,
        mimetype="text/csv",
        headers={"Content-Disposition": "attachment; filename=export.csv"},
    )

The Content-Disposition header tells the browser to download the file.

Other options you may need

The general lesson

CSV looks simple, but its edge cases are in the data, not in your test file with clean names. When a format has a parser and a writer in the standard library, use them, and test with values that contain the separator, the quote character and a line break.

Do this project yourself. It's ready in a live cloud workspace in your browser - when you finish, it lands on your portfolio.

Start this project - free

What you'll practice

FAQ

How do I write a value with a comma to a CSV file in Python?

Use csv.writer. It wraps any cell that contains a comma in double quotes, so "Smith, Bob" stays one cell. Do not build rows with ",".join().

How do you escape a double quote in CSV?

Write it twice inside a quoted cell. The value O"Reilly is written as "O""Reilly". Python's csv module does this for you.

Why does my CSV file have blank lines between rows?

The file was opened without newline="". csv.writer writes \r\n line endings, and the file object changes the line endings again. Open the file with open(path, "w", newline="").

How do I write a CSV to a string instead of a file in Python?

Create an io.StringIO(), pass it to csv.writer(), write the rows, and call getvalue() on the StringIO to get the text.

Keep learning

Handle Bad CSV Data in PythonBackend projectHandle None Values in a Python ExportBackend projectConvert CSV to Parquet With PyArrowBackend projectBackend roadmapStep by step to hiredBackend interview questionsSTAR answersAll Backend projectsProjects hub

Do this project yourself. It's ready in a live cloud workspace in your browser - when you finish, it lands on your portfolio.

Start this project - free
Start this project - free