CSV semantics: RFC 4180 vs Python

csv · Expert · 10 min

Interoperability bugs come from small differences between what producers write and what parsers accept.

There is no single CSV standard. RFC 4180 (2005) documents the common format: records separated by CRLF, fields separated by commas, fields containing commas, quotes or line breaks enclosed in double quotes, and quotes inside quoted fields doubled. An optional header line has the same format.

Python's default excel dialect matches it closely, with deliberate leniencies:

  • Line endings: the writer emits \r\n; the reader accepts \r\n, \n or \r. Inside quoted fields, line breaks are kept exactly, which only works if the file object doesn't translate them, hence newline="".
  • Whitespace is significant: a, b gives ' b' unless skipinitialspace=True. RFC 4180 agrees: spaces are part of the field.
  • Empty lines are returned as empty lists by reader and skipped by DictReader.
  • Quotes inside unquoted fields (ab"c) are kept literally in non-strict mode; RFC 4180 doesn't allow them.
  • Final line may or may not end with a newline; both parse the same.
  • RFC 4180 says nothing about encoding (it registers text/csv with an optional charset), types or nulls, which is why QUOTE_NOTNULL / QUOTE_STRINGS are Python conventions, not portable semantics.

When exchanging data, document dialect, encoding, header and null conventions explicitly.

Example

import csv, io

cases = {
    "mixed endings": "a,b\r\nc,d\ne,f\rg,h",
    "spaces":        "a, b ,c",
    "blank lines":   "a\n\nb\n",
    "stray quote":   'ab"c,d',
    "quoted break":  '"line1\r\nline2",x',
}
for name, text in cases.items():
    print(f"{name:14}", list(csv.reader(io.StringIO(text, newline=""))))
# mixed endings  [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
# spaces         [['a', ' b ', 'c']]
# blank lines    [['a'], [], ['b']]
# stray quote    [['ab"c', 'd']]
# quoted break   [['line1\r\nline2', 'x']]
print(list(csv.DictReader(io.StringIO("h\n\n1\n"))))    # [{'h': '1'}]  (blank skipped)

Remember

  • Python's excel dialect closely follows RFC 4180, but its reader is more lenient
  • Whitespace is data; blank lines give [] in reader and are skipped by DictReader
  • Encoding, types and nulls are outside the spec: document them

Watch out for

  • Assuming every producer writes RFC 4180; validate stray quotes and spaces from hand-made files.

Check yourself

  1. What does csv.reader return for the line 'a, b'?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.rfc-editor.org/rfc/rfc4180

Builds on

Unlocks

Nothing builds on this yet.