Python’s built-in csv module needs no install. Open the file with newline=''. Use csv.reader or csv.DictReader to read, and csv.writer or csv.DictWriter to write. Every value you read comes back as a string, and you convert types yourself. The examples below follow the official csv documentation.
The minimal read and write pattern
import csv
with open("input.csv", newline="", encoding="utf-8") as f:
for row in csv.reader(f):
print(row) # a list of strings
with open("output.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["name", "score"])
writer.writerow(["Ada", 98])
Two details matter here:
newline=''is recommended by the documentation for both reading and writing. It lets the csv module handle line endings itself, including newlines inside quoted fields.encodingis your choice. The module works on strings and does not pick a file encoding. Pass the one that matches the file, such asutf-8.
Reading and writing rows as dictionaries
DictReader takes its keys from the first row, and that row is not returned as data. DictWriter requires an explicit fieldnames list, which sets the column order. Call writeheader() if you want a header row.
with open("people.csv", newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
print(row["first_name"], row["last_name"])
with open("people_out.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["first_name", "last_name"])
writer.writeheader()
writer.writerow({"first_name": "Ada", "last_name": "Lovelace"})
Use writerows() to write an iterable of rows in one call.
Which class to use
| Need | Reading | Writing |
|---|---|---|
| Positional rows (lists) | csv.reader |
csv.writer |
| Named columns (dicts) | csv.DictReader |
csv.DictWriter |
| Header source | First row, or pass fieldnames |
Always pass fieldnames |
Dictionary access survives column reordering and is easier to read. Lists are simpler for headerless files.
#1 Best Overall
Values are strings until you convert them
csv.reader does not infer integers, dates or anything else. Convert after parsing:
for row in csv.DictReader(f):
score = int(row["score"])
On the writing side, non-string values are passed through str(). None becomes an empty string, and the documentation notes this cannot be reversed when you read the file back.
Rank #2
Records are not the same as lines
A quoted field can contain newlines, so one record can span several physical lines. The reader’s line_num counts source lines consumed, not records returned. Keep this in mind when you report error positions.
Handling other formats: delimiters and dialects
The defaults describe the Excel dialect. They are not a universal CSV standard. For other files, pass options directly or define a dialect:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →csv.reader(f, delimiter=";")
csv.reader(f, delimiter="t")
The dialect settings are:
- a one-character
delimiterandquotechar - an
escapechar doublequoteskipinitialspacestrict- the quoting mode
- the writer’s
lineterminator
The reader recognizes r or n as line endings and ignores lineterminator.
Quoting modes
| Constant | Behavior |
|---|---|
QUOTE_MINIMAL |
Quotes only fields containing special characters. |
QUOTE_ALL |
Quotes every field. |
QUOTE_NONNUMERIC |
Quotes nonnumeric values when writing. When reading, converts unquoted fields to float. This is not general type inference. |
QUOTE_NONE |
Disables quote processing. Writing data that needs escaping requires an escapechar. |
QUOTE_NOTNULL, QUOTE_STRINGS |
Special handling of None and empty unquoted values. Added in Python 3.12, so check your runtime and the consuming application. |
Missing and extra fields
DictReader: extra values in a row are stored in a list underrestkey(defaultNone). Missing values are filled withrestval(defaultNone).DictWriter: a dictionary key not infieldnamesraises an error by default (extrasaction='raise'). Setextrasaction='ignore'to drop it.restvalsupplies the output value for missing keys.
Guessing the format with Sniffer
csv.Sniffer().sniff(sample) returns a guessed dialect from a text sample. has_header(sample) estimates whether the first row is a header, and the documentation warns it can give false positives and negatives. When you know the data contract, configure the format explicitly. Reserve the sniffer for files from unknown sources, and verify its result.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




