Explain ragged CSV rows with record-level evidence
Also published in our Blogger archive.
Quick answer
csv.reader yields a sequence for each record and does not impose a table width.
Example
The fixture compares each record length with the header length and prints record number, expected width, and actual width. It deliberately reports both a long row and a short row instead of padding or truncating either one.
import csv, io, json
rows = list(csv.reader(io.StringIO('id,name\n1,A\n2,B,x\n3\n')))
findings = [{'record': n, 'expected': 2, 'actual': len(row)} for n, row in enumerate(rows[1:], 2) if len(row) != 2]
assert findings == [{'record': 3, 'expected': 2, 'actual': 3}, {'record': 4, 'expected': 2, 'actual': 1}]
print(json.dumps(findings, separators=(',', ':')))
Expected stdout:
[{"record":3,"expected":2,"actual":3},{"record":4,"expected":2,"actual":1}]
Reading the result
If the export permits a trailing optional field, express that as an explicit schema version. A width mismatch alone cannot tell a validator which interpretation is intended.
Use parser record numbers rather than file-line numbers in this report. A quoted value can span physical lines, so those two coordinates have different meanings in a CSV investigation.
The expected width comes from the first parsed record only after that record has been accepted as the header. A preamble needs an explicit rule before it can serve that role.
Sources
- Python csv module documentation
Prepared with AI assistance. The example uses synthetic data; its stated limits apply.