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=""aopen. 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 sopraint(...). 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=""aopenquando leggi o scrivi CSV. - Usa
DictReader/DictWriterogni 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.