← Debugging Guides

阅读中文版本

Python csv — Extra Blank Lines in Output on Windows

Fix doubled newlines in csv.writer output on Windows by understanding text-mode translation, and open files with newline="" as the csv module requires.

beginnerdebuggingpythoncsvwindows

Symptoms

  • CSV files written on Windows open with an empty line between every data row in Excel or pandas
  • pandas.read_csv reports skipping N lines or empty-field warnings on files the script just wrote
  • The same script on Linux produces a clean file with half the line count

Possible causes

  • csv.writer emits \r\n line terminators, and a text-mode file object translates the \n into another \r\n, doubling every newline
  • The file was opened without newline="", so the platform default translation applies to writes
  • Excel-era expectations made someone set lineterminator manually, conflicting with open() defaults

Diagnosis steps

Count lines and inspect bytes

wc -l out.csv && od -c out.csv | head -4

Interpretation

Line count double the rows plus od showing \r \r \n sequences proves translation doubling rather than stray data.

Check the open call

grep -Rn "open(.*csv\|csv.writer" src/ | grep -v "newline=" | head

Interpretation

An open() without newline= feeding csv.writer is the exact combination the csv docs warn about.

Verify with the minimal loop

python -c "import csv; f=open('t.csv','w',newline=''); csv.writer(f).writerows([[1,2],[3,4]]); f.close()" && od -c t.csv | head -3

Interpretation

Clean \r \n line endings with newline="" confirm the fix produces the documented byte layout.

Root cause

The csv module performs its own line termination and writes \r\n. A file opened in default text mode translates each \n on Windows into \r\n again, so every row ends with \r\r\n and readers see an empty line between data rows.

Fix

Open CSV files for writing with newline="" to disable translation, letting the csv module own the byte-level line endings. When generating CSV as a string, use io.StringIO(newline="") for the same reason.

Risk note

Reading with newline="" is equally required for files containing embedded newlines inside quoted fields; skipping it silently corrupts rows.

Verify the fix

Write a fixture with quoted multi-line fields on Windows, assert byte-exact \r\n row endings with od, and round-trip through csv.reader and pandas.read_csv with zero warnings.

Symptom walkthrough

The symptom is visible in the raw bytes before any parser complains:

$ od -c out.csv | head -3
0000000   i   d   ,   n   a   m   e   \r  \r  \n   1   ,   a   \r  \r  \n

\r\r\n is the fingerprint of translation doubling: csv.writer wrote \r\n, and the text-mode file object translated the \n half into a second \r\n. Line counts confirm it — the Windows file has exactly twice as many lines as the Linux run of the same script. Every second line being empty in Excel is the user-visible version of the same bytes.

Fix in practice

One parameter, documented but routinely skipped:

import csv

with open("out.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["id", "name"])
    writer.writerows(rows)
# byte endings are now exactly \r\n — csv owns them

The same rule applies in memory when you build CSV strings:

import io

buf = io.StringIO(newline="")
csv.writer(buf).writerows(rows)
payload = buf.getvalue()

And the reading direction needs it too, for a different reason: quoted fields containing embedded newlines stay intact only when newline=“” prevents premature line splitting.