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

    Programador pensando enquanto analisa código em dois monitores
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Learn Programming from Scratch: Beginner Guide

    Learn programming from scratch with a practical roadmap covering logic, languages, tools, exercises, projects, debugging, Git, and consistent study habits.

    Ler mais

    Tempo de leitura: 6 minutos
    10/07/2026
    ícone de loop com o texto 'For' abaixo
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python for Loops: Complete Beginner Guide

    Learn Python for loops with sequences, range, enumerate, zip, dictionaries, nested loops, break, continue, comprehensions, and practical examples.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    Manipulação e formatação de strings em Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Strings: Complete Beginner Guide

    Learn Python strings with creation, indexing, slicing, methods, formatting, Unicode, searching, validation, splitting, joining, and practical examples.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    código Python de uma tupla com o texto separado formando a frase "O que são tuplas?"
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Tuples: Complete Beginner Guide

    Learn Python tuples with creation, packing, unpacking, indexing, immutability, methods, named records, function returns, and practical examples.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    símbolo de PI
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Floats: Complete Beginner Guide

    Learn Python floats with decimal values, arithmetic, conversion, rounding, precision limits, comparisons, Decimal, math functions, and practical examples.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    notebook mostrando tela a teclado
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Input and Output: Complete Guide

    Learn Python input and output with input(), print(), conversions, validation, formatting, files, command-line arguments, and practical interactive programs.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026