W3docs

File CSV in Python

Impara a leggere e scrivere file CSV in Python con il modulo csv integrato: csv.reader, csv.writer, DictReader e DictWriter con esempi pratici.

CSV (Comma-Separated Values) è uno dei formati più comuni per lo scambio di dati tabulari — ogni applicazione per fogli di calcolo, database e strumento di data science è in grado di leggerlo e scriverlo. Il modulo csv integrato in Python gestisce automaticamente le parti complesse: la quotatura dei campi che contengono virgole, la gestione dei caratteri di nuova riga tra i diversi sistemi operativi e la mappatura delle righe in dizionari. Non è necessario installare nulla; csv è incluso in ogni installazione Python.

Questo capitolo tratta la lettura di file CSV, la scrittura di file CSV, l'utilizzo di DictReader e DictWriter, la gestione di delimitatori personalizzati e i problemi comuni da evitare.

Cos'è un File CSV?

Un file CSV è un file di testo normale in cui ogni riga rappresenta una riga di dati e ogni campo all'interno di una riga è separato da un delimitatore — solitamente una virgola. Ecco un esempio minimale:

name,age,city
Alice,30,New York
Bob,25,London

La prima riga è tipicamente un'intestazione che assegna un nome a ogni colonna. Le righe successive contengono i dati effettivi. Se il valore di un campo contiene a sua volta una virgola, il campo viene racchiuso tra virgolette doppie:

name,bio
Alice,"Engineer, New York"

Il modulo csv gestisce questa quotatura in modo trasparente, quindi non è necessario analizzarla manualmente.

Lettura di File CSV con csv.reader

csv.reader trasforma un file aperto (o qualsiasi iterabile di stringhe) in un iteratore che restituisce ogni riga come lista Python.

Schema di base — lettura di un file CSV riga per riga

import csv

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

L'argomento newline="" è importante. Senza di esso, la traduzione universale dei caratteri di nuova riga di Python può inserire righe vuote aggiuntive su Windows, poiché il modulo csv gestisce internamente la traduzione dei caratteri di nuova riga.

Supponendo che people.csv contenga i dati dell'esempio precedente, l'output è:

['name', 'age', 'city']
['Alice', '30', 'New York']
['Bob', '25', 'London']

Si noti che tutti i valori — incluso il numero 30 — vengono restituiti come stringhe. Il modulo csv non deduce i tipi di dato; è necessario convertirli manualmente quando occorre.

Saltare la Riga di Intestazione

Quando si vogliono solo le righe di dati e non l'intestazione, si chiama next() sul reader una volta per consumare la prima riga:

Saltare la riga di intestazione con next()

import csv

with open("people.csv", newline="") as f:
    reader = csv.reader(f)
    header = next(reader)          # consume and store the header
    print("Columns:", header)
    for row in reader:             # only data rows remain
        name, age, city = row
        print(f"{name} is {age} years old and lives in {city}.")

Output:

Columns: ['name', 'age', 'city']
Alice is 30 years old and lives in New York.
Bob is 25 years old and lives in London.

Caricare Tutte le Righe in una Lista

Se si ha bisogno dell'intero file in memoria in una volta sola, si passa il reader a list():

import csv

with open("people.csv", newline="") as f:
    reader = csv.reader(f)
    rows = list(reader)

print(rows[0])   # header row
print(rows[1])   # first data row

Output:

['name', 'age', 'city']
['Alice', '30', 'New York']

Scrittura di File CSV con csv.writer

csv.writer scrive righe su qualsiasi oggetto simile a un file, quotando automaticamente i campi che contengono il delimitatore, virgolette doppie o caratteri di nuova riga.

Scrittura di righe in un nuovo file CSV

import csv

rows = [
    ["product", "price", "quantity"],
    ["Apple", 1.2, 50],
    ["Banana", 0.5, 100],
    ["Cherry", 3.0, 30],
]

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

Dopo l'esecuzione, inventory.csv contiene:

product,price,quantity
Apple,1.2,50
Banana,0.5,100
Cherry,3.0,30

Si usa writer.writerow(row) per scrivere una singola riga, oppure writer.writerows(rows) per scriverne molte in una volta. Entrambi accettano qualsiasi iterabile.

Perché newline="" è Importante in Scrittura

Su Windows, Python apre i file di testo in una modalità che traduce \n in \r\n. Il modulo csv scrive anche \r\n come terminatore di riga per impostazione predefinita. Insieme producono \r\r\n — una riga vuota tra ogni riga quando il file viene aperto in un altro programma. Passare newline="" sopprime la traduzione aggiuntiva e lascia che csv gestisca autonomamente i terminatori di riga.

Lettura di File CSV con csv.DictReader

DictReader mappa ogni riga in un OrderedDict (o dict semplice in Python 3.8+) con chiavi corrispondenti ai nomi delle colonne nella riga di intestazione. Questo è l'approccio preferito quando le colonne hanno nomi significativi e si vuole accedervi per nome anziché per indice.

Lettura di un file CSV come sequenza di dizionari

import csv

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

Output:

Alice — New York
Bob — London

DictReader legge automaticamente la prima riga come intestazione. È possibile sovrascrivere questo comportamento passando un argomento fieldnames:

import csv

# File has no header; provide field names explicitly
with open("data_no_header.csv", newline="") as f:
    reader = csv.DictReader(f, fieldnames=["name", "age", "city"])
    for row in reader:
        print(row)

L'attributo reader.fieldnames contiene sempre la lista dei nomi delle colonne in uso, utile per l'ispezione prima di elaborare le righe.

Scrittura di File CSV con csv.DictWriter

DictWriter è il corrispondente di DictReader. Si forniscono i nomi delle colonne in anticipo, poi si scrivono dizionari — il writer mappa ogni chiave nella colonna corretta.

Scrittura di una lista di dizionari in un file CSV

import csv

people = [
    {"name": "Alice", "age": 30, "city": "New York"},
    {"name": "Bob", "age": 25, "city": "London"},
]

fieldnames = ["name", "age", "city"]

with open("people_out.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()          # writes the column-name row
    writer.writerows(people)

Il file risultante:

name,age,city
Alice,30,New York
Bob,25,London

writeheader() utilizza la lista fieldnames fornita durante la costruzione. Va chiamato una sola volta prima di qualsiasi chiamata a writerow().

Gestione di Chiavi Extra o Mancanti

Per impostazione predefinita, DictWriter lancia un ValueError se un dizionario contiene una chiave non presente in fieldnames. È possibile modificare questo comportamento con il parametro extrasaction:

writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")

Viceversa, se un dizionario non ha una chiave, il writer scrive una stringa vuota per quel campo, a meno che non si fornisca un valore predefinito con restval:

writer = csv.DictWriter(f, fieldnames=fieldnames, restval="N/A")

Delimitatori e Quotatura Personalizzati

I file CSV reali non sono sempre delimitati da virgole. I file con valori separati da tabulazione (TSV) e i file delimitati da pipe sono comuni. Usare il parametro delimiter per gestirli:

Lettura di un file separato da tabulazioni

import csv

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

Scrittura di un file delimitato da pipe

import csv

with open("output.psv", "w", newline="") as f:
    writer = csv.writer(f, delimiter="|")
    writer.writerow(["id", "name", "score"])
    writer.writerow([1, "Alice", 98])
    writer.writerow([2, "Bob", 87])

File di output:

id|name|score
1|Alice|98
2|Bob|87

Costanti di Quotatura

Il parametro quoting controlla quali campi vengono quotati nell'output:

CostanteValoreComportamento
csv.QUOTE_MINIMAL0Quota solo i campi che contengono il delimitatore, il carattere di quotatura o un carattere di nuova riga (predefinito)
csv.QUOTE_ALL1Quota ogni campo
csv.QUOTE_NONNUMERIC2Quota tutti i campi non numerici; il reader converte i campi non quotati in float
csv.QUOTE_NONE3Non quota mai; genera un errore se il delimitatore compare in un campo

Forza la quotatura di ogni campo

import csv, io

output = io.StringIO()
writer = csv.writer(output, quoting=csv.QUOTE_ALL)
writer.writerow(["name", "bio"])
writer.writerow(["Alice", "Engineer, New York"])
print(output.getvalue())

Output:

"name","bio"
"Alice","Engineer, New York"

Uso di io.StringIO per CSV in Memoria

Quando non è necessario accedere al filesystem — ad esempio nei test o quando si elaborano dati CSV ricevuti da un API — si usa io.StringIO come oggetto simile a un file:

Analisi di CSV da una stringa

import csv
import io

raw = "name,score\nAlice,95\nBob,87\n"

reader = csv.DictReader(io.StringIO(raw))
for row in reader:
    print(row["name"], "scored", row["score"])

Output:

Alice scored 95
Bob scored 87

Problemi Comuni

Tutti i Valori Sono Stringhe

csv.reader e DictReader restituiscono sempre stringhe. Convertire i valori esplicitamente:

age = int(row["age"])
price = float(row["price"])

Problemi di Codifica

Aprire i file con la codifica corretta per evitare UnicodeDecodeError. UTF-8 è la codifica più comune per i file CSV moderni, ma i file esportati da Excel possono usare latin-1 o cp1252:

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

Righe Vuote

Se il file CSV contiene righe vuote tra le righe di dati, csv.reader restituisce liste vuote [] per esse. Filtrarle:

import csv

with open("data.csv", newline="") as f:
    reader = csv.reader(f)
    for row in reader:
        if not row:        # skip blank lines
            continue
        print(row)

Gestione degli Errori

Racchiudere le operazioni sui file in un blocco try/except per gestire correttamente i file mancanti e gli errori di permesso:

import csv

try:
    with open("data.csv", newline="") as f:
        reader = csv.reader(f)
        for row in reader:
            print(row)
except FileNotFoundError:
    print("Error: data.csv was not found.")
except PermissionError:
    print("Error: no permission to read data.csv.")

csv vs Pandas per File di Grandi Dimensioni

Il modulo csv è ideale per:

  • File di piccole e medie dimensioni (fino a qualche centinaio di MB)
  • Script che non hanno Pandas installato
  • Situazioni in cui si necessita di un controllo preciso sulla lettura e scrittura

Per dataset di grandi dimensioni, filtraggio complesso o operazioni di aggregazione, la libreria di terze parti pandas fornisce pd.read_csv() e DataFrame.to_csv(), che sono significativamente più veloci e ricche di funzionalità.

Mettere Tutto Insieme

L'esempio seguente legge un file CSV, filtra le righe in base a una condizione e scrive i risultati filtrati in un nuovo file:

Filtrare le righe e scrivere un nuovo file CSV

import csv

input_file = "inventory.csv"
output_file = "expensive.csv"

with open(input_file, newline="") as infile, \
     open(output_file, "w", newline="") as outfile:

    reader = csv.DictReader(infile)
    writer = csv.DictWriter(outfile, fieldnames=reader.fieldnames)

    writer.writeheader()
    for row in reader:
        if float(row["price"]) >= 1.0:
            writer.writerow(row)

print(f"Filtered rows written to {output_file}.")

Questo schema — aprire entrambi i file nello stesso blocco with, trasmettere le righe dal reader al writer — gestisce file di qualsiasi dimensione senza caricare tutto in memoria in una volta.

Capitoli Correlati

Pratica

Pratica
Which csv module class maps each CSV row to a dictionary keyed by column names?
Which csv module class maps each CSV row to a dictionary keyed by column names?
Was this page helpful?