Python CSV Files: Read, Write, and Process Data

Published on: July 10, 2026
Reading time: 5 minutes
Leitura e escrita de arquivos CSV em Python

CSV files are everywhere: spreadsheet exports, sales reports, contact lists, analytics downloads, and database transfers. Although the name means “comma-separated values,” real files may use semicolons, tabs, quoted fields, or embedded line breaks. Python’s standard csv module handles those details more safely than manually splitting strings.

This guide shows how to read, write, filter, validate, and transform Python CSV files with both list-based and dictionary-based APIs. It also explains encoding, delimiters, missing values, and the point at which a dataframe library may be more appropriate.

Why not use split(“,”)?

A simple line.split(",") fails when a value contains a comma inside quotes, when quotes are escaped, or when a field spans multiple lines. The standard parser understands these conventions and can adapt to different dialects. The official Python csv documentation is the authoritative reference.

Read a CSV file with csv.reader

Open the file with newline="" so the module can manage newline conventions correctly. Declare the text encoding explicitly for portability.

import csv

with open("sales.csv", newline="", encoding="utf-8") as file:
    reader = csv.reader(file)

    for row in reader:
        print(row)

Each row is returned as a list of strings. The parser does not automatically know that a column represents an integer, decimal, date, or Boolean. Convert and validate fields according to your schema.

import csv

total = 0.0

with open("sales.csv", newline="", encoding="utf-8") as file:
    reader = csv.reader(file)
    header = next(reader)

    for product, quantity_text, price_text in reader:
        quantity = int(quantity_text)
        price = float(price_text)
        total += quantity * price

print(f"Total: ${total:.2f}")

Calling next(reader) consumes the header row. For a more maintainable approach, use DictReader so code refers to column names instead of positions.

Read rows as dictionaries

import csv

with open("people.csv", newline="", encoding="utf-8") as file:
    reader = csv.DictReader(file)

    for row in reader:
        print(row["name"], row["email"])

When fieldnames is omitted, the first row supplies the keys. This makes code easier to read and more resilient when columns move, although renamed or missing columns still require validation. Our Python dictionaries guide explains the mapping operations used here.

Write CSV data

Use csv.writer for sequences. writerow() writes one record and writerows() writes an iterable of records.

import csv

rows = [
    ["name", "score"],
    ["Maya", 92],
    ["Noah", 87],
    ["Liam", 95],
]

with open("scores.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.writer(file)
    writer.writerows(rows)

Opening with mode w replaces the existing file. Use the practices from the text file guide to choose modes and handle errors safely.

Write dictionaries with DictWriter

import csv

records = [
    {"name": "Maya", "score": 92},
    {"name": "Noah", "score": 87},
]

fieldnames = ["name", "score"]

with open("scores.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(records)

DictWriter requires an explicit field order. By default, an unexpected key raises an error, which is often useful because it reveals a mismatch between the data and file schema.

Handle semicolon and tab delimiters

Not every CSV uses commas. Spreadsheet software in some locales exports semicolon-delimited files, while tab-separated values use a tab character.

import csv

with open("products.csv", newline="", encoding="utf-8") as file:
    reader = csv.DictReader(file, delimiter=";")

    for row in reader:
        print(row)

Specify the format when you know it. csv.Sniffer can estimate a dialect from a sample, but it is a heuristic and should not replace validation in critical workflows.

Filter and transform rows

A common pipeline reads rows, validates them, filters records, and writes a clean output file. The next example keeps active users and normalizes names and emails.

import csv

with open("users.csv", newline="", encoding="utf-8") as source:
    reader = csv.DictReader(source)

    cleaned = []
    for row in reader:
        if row["active"].strip().lower() != "yes":
            continue

        cleaned.append({
            "name": row["name"].strip().title(),
            "email": row["email"].strip().lower(),
        })

with open("active_users.csv", "w", newline="", encoding="utf-8") as target:
    writer = csv.DictWriter(target, fieldnames=["name", "email"])
    writer.writeheader()
    writer.writerows(cleaned)

For a large input, write each cleaned record immediately instead of storing the entire cleaned list. Streaming keeps memory usage predictable.

Stream large files

import csv

with (
    open("events.csv", newline="", encoding="utf-8") as source,
    open("errors.csv", "w", newline="", encoding="utf-8") as target,
):
    reader = csv.DictReader(source)
    writer = csv.DictWriter(target, fieldnames=reader.fieldnames or [])
    writer.writeheader()

    for row in reader:
        if row.get("level") == "ERROR":
            writer.writerow(row)

File objects and CSV readers are iterators, so rows are produced as needed. This is more scalable than list(reader). The guide to generators and yield explains lazy data flow further.

Validate required columns

Do not assume every file has the expected header. Check required columns before processing records and produce a clear error.

import csv

required = {"name", "email", "active"}

with open("users.csv", newline="", encoding="utf-8") as file:
    reader = csv.DictReader(file)
    actual = set(reader.fieldnames or [])
    missing = required - actual

    if missing:
        names = ", ".join(sorted(missing))
        raise ValueError(f"Missing CSV columns: {names}")

    for row in reader:
        print(row["email"])

Set operations make schema comparisons concise. Learn union, intersection, and difference in the Python sets tutorial.

Handle bad rows without hiding errors

A production import should report the line and reason when conversion fails. Decide whether one invalid row should stop the job or be recorded and skipped.

import csv

with open("orders.csv", newline="", encoding="utf-8") as file:
    reader = csv.DictReader(file)

    for row_number, row in enumerate(reader, start=2):
        try:
            quantity = int(row["quantity"])
            price = float(row["price"])
        except (KeyError, TypeError, ValueError) as error:
            print(f"Row {row_number} skipped: {error}")
            continue

        print(quantity * price)

Starting at two accounts for the header line. Avoid a blanket except, which could also hide programming bugs. See Python exception handling for specific handlers.

Dates and missing values

CSV has no native data types. Empty fields, the text NULL, and omitted values may all mean different things. Define a conversion policy. Parse date strings deliberately rather than comparing them lexicographically unless they use a guaranteed sortable format such as ISO 8601.

from datetime import date

def parse_date(value: str) -> date | None:
    value = value.strip()

    if not value:
        return None

    return date.fromisoformat(value)

The datetime guide covers parsing, formatting, and date arithmetic.

CSV versus pandas

The standard module is ideal for streaming, simple transformations, low dependencies, and precise format control. Pandas is often more productive for exploratory analysis, joins, group operations, missing-data cleanup, and columnar calculations. It loads data into a dataframe and introduces a larger dependency.

Choose based on the task. The site’s Pandas introduction is a good next step for analytical workflows.

Common mistakes

  • Parsing rows with split(",") instead of the CSV module.
  • Forgetting newline="" when opening CSV files.
  • Assuming values are automatically converted to numbers or dates.
  • Ignoring encoding and receiving unreadable characters.
  • Loading an enormous file into a list unnecessarily.
  • Trusting headers and row lengths without validation.
  • Overwriting the source file before a transformation is verified.

Frequently asked questions

How do I read an Excel file?

An .xlsx workbook is not CSV. Use a library designed for Excel files, such as openpyxl or pandas. The guide to editing Excel spreadsheets with Python explains that workflow.

Why are all values strings?

CSV stores textual fields and does not carry a universal schema. Convert each field according to your application’s rules and report invalid values clearly.

Can a CSV field contain a comma?

Yes. The writer quotes fields when required, and the reader recognizes properly quoted delimiters. That is a key reason to use the module instead of manual string splitting.

Final thoughts

Reliable Python CSV processing starts with the standard parser, explicit UTF-8 encoding, newline="", documented column rules, and specific error reporting. Stream large files, validate headers before accessing them, and move to a dataframe library only when its higher-level analysis features provide real value.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Uso do operador in em Python para verificação em coleções
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python in Operator: Membership Tests Explained

    Learn the Python in operator with strings, lists, sets, dictionaries, ranges, generators, custom classes, not in, and efficient membership.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Introdução ao Python para iniciantes
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python List Comprehensions: Complete Beginner Guide

    Learn Python list comprehensions with transformations, filters, conditions, nested loops, dictionaries, sets, generators, and style tips.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Fatiamento de listas (slicing) em Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Slicing: Complete Guide with Examples

    Master Python slicing with start, stop, step, negative indexes, reversal, shallow copies, slice assignment, deletion, and practical examples.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Pessoa usando tablet com caneta digital para planejar tarefas em checklist, representando organização, planejamento e produtividade digital.
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python sort() vs sorted(): Complete Guide

    Understand Python sort() vs sorted(), including mutation, custom keys, reverse order, stable sorting, multiple fields, and common mistakes.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Leitura e escrita de arquivos TXT usando Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Read and Write Text Files in Python

    Learn to read, write, append, and process text files in Python with UTF-8 encoding, context managers, pathlib, and practical examples.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Uso da função enumerate em loops Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python enumerate(): Cleaner Loops with Indexes

    Learn Python enumerate() to loop with indexes, choose a custom start value, combine it with zip, process files, and avoid

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026