Menu

CSV in Python: leggere e scrivere file CSV con il modulo csv

Come leggere e scrivere file CSV in Python: il modulo csv, DictReader e DictWriter, intestazioni, virgolette e quando passare invece a pandas.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Il CSV è più semplice di quanto sembri (e più insidioso di quanto pensi)

Un file CSV è solo testo: righe separate da a capo, campi separati da virgole. Questa è l'idea. In pratica incontri stringhe tra virgolette che contengono virgole, campi con a capo al loro interno, convenzioni regionali diverse (virgola o punto e virgola), file salvati da Excel con un BOM: abbastanza casi limite da rendere quasi sempre un errore scriverti da solo un line.split(",").

Il modulo integrato csv di Python gestisce tutto questo. Per file piccoli e medi raramente ti servirà altro.

Leggere un CSV con csv.reader

csv.reader restituisce ogni riga come lista di stringhe:

import csv

with open("people.csv", newline="") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

Qualche dettaglio non ovvio:

  • Passa sempre newline="" a open. Il modulo csv gestisce da solo i fine riga; senza questo, su Windows ottieni righe vuote in più.
  • Ogni valore è una stringa. "42" resta una stringa finché non ci chiami sopra int(...). Il CSV non ha tipi.
  • La riga di intestazione è una riga come le altre. Se il file ha un'intestazione, salta la prima riga a mano oppure passa a DictReader.

Saltare la riga di intestazione

import csv

with open("people.csv", newline="") as f:
    reader = csv.reader(f)
    headers = next(reader)         # estrae la prima riga
    for row in reader:
        print(row)

next(reader) fa avanzare l'iteratore di uno e restituisce quella riga.

Leggere come dict con DictReader

csv.DictReader tratta la prima riga come intestazione e ti restituisce ciascuna delle righe successive come dict:

import csv

with open("people.csv", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row["name"], row["email"])

È quasi sempre quello che ti serve. I nomi delle colonne si spiegano da soli, e riordinare le colonne nel file di origine non rompe il tuo codice.

Se il file non ha un'intestazione, passala esplicitamente con fieldnames=["name", "email", ...].

Scrivere un CSV con csv.writer

csv.writer trasforma righe (liste) in righe CSV:

import csv

rows = [
    ["name", "age", "city"],
    ["Rosa", 30, "Lisbon"],
    ["Ada", 36, "London"],
]

with open("out.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(rows)

writerow(row) scrive una sola riga; writerows(rows) scrive un intero iterabile in una volta. Entrambi mettono automaticamente tra virgolette i campi che contengono virgole, virgolette o a capo: non devi pensarci tu.

Scrivere dict con DictWriter

Quando i tuoi dati sono già sotto forma di dict, DictWriter ti evita il passaggio "converti in lista":

import csv

people = [
    {"name": "Rosa", "age": 30, "city": "Lisbon"},
    {"name": "Ada", "age": 36, "city": "London"},
]

with open("out.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
    writer.writeheader()
    writer.writerows(people)

L'argomento fieldnames controlla sia l'intestazione sia l'ordine delle colonne. Le chiavi dei tuoi dict che non sono in fieldnames vengono scartate in silenzio (oppure puoi far sollevare un errore con extrasaction="raise").

Delimitatori e virgolette diversi

Non tutti i "CSV" usano le virgole. Le impostazioni europee usano spesso ;, i file separati da tabulazioni usano \t, alcuni sistemi usano |. Passa un argomento delimiter=:

import csv

with open("data.tsv", newline="") as f:
    reader = csv.reader(f, delimiter="\t")
    for row in reader:
        print(row)

Per file con regole di virgolette insolite, csv.register_dialect(...) ti permette di configurare una volta e riutilizzare. Per la maggior parte dei file bastano le impostazioni predefinite più delimiter=.

Codifica

I file CSV sono testo, quindi hanno una codifica. UTF-8 è lo standard moderno; i file nati da Excel su Windows a volte usano cp1252 o includono un BOM UTF-8. Sii esplicito:

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

Se vedi UnicodeDecodeError, il file non ha la codifica che pensavi. Prova utf-8-sig (gestisce il BOM di Excel), cp1252 o latin-1, i sospettati più comuni.

Trasformare le righe CSV in tipi utili

Visto che ogni valore arriva come stringa, la conversione tocca a te:

(StringIO ci permette di eseguire l'esempio senza un file vero; nel codice reale useresti open(path).)

Per CSV con tipi complicati (date, numeri che possono mancare, vero/falso scritti in dieci modi diversi), valuta pandas: ha già delle convenzioni per la maggior parte di questi casi.

Quando passare a pandas

pandas.read_csv(path) restituisce un DataFrame, che è la struttura giusta appena vuoi:

  • Filtrare righe: df[df["active"] == True]
  • Aggregare: df.groupby("city")["age"].mean()
  • Unire con un'altra tabella
  • Riscrivere il risultato con una formattazione semplice
import pandas as pd

df = pd.read_csv("people.csv")
adults = df[df["age"] >= 18]
adults.to_csv("adults.csv", index=False)

Pandas è eccessivo per letture piccole e lineari, ed è una dipendenza pesante (installalo con pip in un ambiente virtuale). Ma per qualsiasi cosa abbia la forma di dati da analizzare, è lo strumento a cui si rivolge la maggior parte degli analisti Python.

Elaborare file molto grandi in streaming

csv.reader è già lazy: legge una riga alla volta. Mantienilo così iterando (senza chiamare subito list(reader)), e la memoria usata resta costante a prescindere dalla dimensione del file:

import csv

with open("huge.csv", newline="") as f:
    reader = csv.DictReader(f)
    error_count = 0
    for row in reader:
        if row["status"] == "error":
            error_count += 1

print(f"Found {error_count} errors.")

Gestisce un file da 10 GB con la stessa tranquillità di uno da 10 KB, purché tu non accumuli le righe in una lista.

Qualche buona abitudine

  • Passa sempre newline="" a open quando leggi o scrivi CSV.
  • Usa DictReader/DictWriter ogni volta che il file ha un'intestazione: è più leggibile degli indici numerici.
  • Sii esplicito sulla codifica, soprattutto con file da Excel o da fonti non inglesi.
  • Converti i tipi già in fase di lettura, così il codice successivo non deve farlo.
  • Passa a pandas quando vuoi analizzare i dati, non solo spostarli.

Prossimo argomento

Ora sai leggere JSON e CSV. L'ultima abilità pratica che vedremo è recuperare dati dalla rete: è il prossimo documento, sulle richieste HTTP con la libreria requests.

Domande frequenti

Come leggo un file CSV in Python?

Usa il modulo integrato csv. csv.reader ti dà ogni riga come lista di stringhe; csv.DictReader usa la prima riga come intestazione e restituisce ogni riga come dict. Apri il file con newline='' così Python non rovina i fine riga: with open('data.csv', newline='') as f:.

Come scrivo un file CSV in Python?

Abbina csv.writer a un file aperto in scrittura con newline=''. Chiama writer.writerow([...]) per ogni riga o writer.writerows([[...], [...]]) per un gruppo di righe. Per dati sotto forma di dict, usa csv.DictWriter: gestisce l'intestazione in automatico.

Meglio il modulo csv o pandas?

Usa csv per letture e scritture veloci, per file che elabori una riga alla volta e quando non vuoi un'altra dipendenza. Usa pandas quando devi filtrare, raggruppare o unire tabelle, o quando il file è abbastanza grande da rendere importanti le operazioni vettoriali. Gestiscono gli stessi file; la scelta dipende da cosa fai con i dati dopo averli caricati.

Perché su Windows il mio CSV ha righe vuote tra una riga e l'altra?

Hai aperto il file senza newline=''. Il modulo csv scrive i propri terminatori di riga; senza quell'argomento, su Windows Python ne aggiunge altri. Apri sempre i file CSV con open(path, newline=''), sia in lettura sia in scrittura.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA