CSV è uno di quei formati che nessuno ama ma tutti usano. Esiste dagli anni '70, è ancora il formato di esportazione predefinito per ogni app di fogli di calcolo del pianeta, e si trova quasi certamente da qualche parte nella tua pipeline dati in questo momento. Il formato sembra banalmente semplice — solo valori separati da virgole — ma analizzarlo correttamente è sorprendentemente facile sbagliare. Vediamo come funziona realmente, dove diventa complicato, e come gestirlo in Python e JavaScript senza farsi del male.

Cos'è Realmente CSV

CSV sta per Comma-Separated Values (Valori Separati da Virgola). Un file CSV è testo puro — nessuna codifica binaria, nessun metadato, nessuna struttura oltre a righe e colonne. Ogni riga è un record, ogni valore all'interno di una riga è separato da un delimitatore (di solito una virgola), e la prima riga è convenzionalmente una riga di intestazione con i nomi delle colonne. Ecco come appare un vero CSV:

text
order_id,customer,product,quantity,unit_price,shipped
1001,Alice Nguyen,USB-C Hub,2,34.99,true
1002,Bob Martinez,Mechanical Keyboard,1,129.00,false
1003,Alice Nguyen,HDMI Cable,3,12.50,true
1004,Carol Smith,Webcam 1080p,1,79.95,false

Ecco tutto — sei colonne, quattro righe dati e un'intestazione. Nessuna parentesi angolare, nessuna parentesi graffa, nessuna virgoletta necessaria (per ora). Questa semplicità è esattamente il motivo per cui CSV persiste: quasi qualsiasi strumento al mondo può aprirlo, leggerlo e produrlo. Excel, Google Sheets, il comando COPY di PostgreSQL, pandas, read.csv() di R — tutti con CSV sin dall'inizio. Il compromesso è che il formato ha quasi nessuna struttura: nessun tipo, nessuna nidificazione, nessuno schema. Ogni valore è una stringa finché non decidi diversamente.

RFC 4180 — Lo Standard che Non è Davvero Applicato

Esiste una specifica: RFC 4180, pubblicata nel 2005. Definisce cose come le terminazioni di riga CRLF, l'escape delle virgolette doppie e come gestire le virgole incorporate. Ma ecco il fatto — RFC 4180 è informativa, non uno standard. Descrive ciò che era già pratica comune, non lo impone. Nessuno è tenuto a seguirla, e in pratica quasi nessuno la segue esattamente.

Il risultato è il caos dei dialetti CSV. Hai file che usano LF invece di CRLF, file dove la prima riga può essere o meno un'intestazione, file con una nuova riga finale e file senza, file con un BOM UTF-8 anteposto da Excel. L' articolo Wikipedia su CSV ha un ottimo riepilogo di tutto ciò che varia in pratica. L'approccio più sicuro: non dare mai per scontato nulla di un file CSV che non hai prodotto tu stesso, e usa sempre un parser adeguato invece di dividere ingenuamente sulle virgole.

Regole di Quotatura: Quando le Virgole Appaiono nei Valori

Qui è dove "dividi sulle virgole" va in pezzi. Cosa succede quando un valore contiene una virgola? O una nuova riga? O una virgoletta doppia? RFC 4180 — e la maggior parte dei parser reali — lo gestisce avvolgendo il campo in virgolette doppie. Ecco il set completo di regole:

  • Se un campo contiene una virgola, una nuova riga o una virgoletta doppia, avvolgi l'intero campo in virgolette doppie
  • Se un campo contiene una virgoletta doppia, eseguine l'escape raddoppiandola ("")
  • I campi tra virgolette possono estendersi su più righe — la nuova riga diventa parte del valore
  • Gli spazi bianchi all'interno delle virgolette sono significativi e devono essere preservati
text
order_id,customer,notes,unit_price
1005,David Lee,"Wants gift wrapping, express shipping",45.00
1006,Emma Brown,"Said: ""please handle with care""",89.99
1007,Frank Wu,"Address:
123 Main St
Apt 4B",15.50

In quell'esempio: le note di David contengono una virgola, quindi è tra virgolette. La nota di Emma contiene virgolette doppie, quindi vengono raddoppiate all'interno delle virgolette esterne. L'indirizzo di Frank si estende su più righe — le interruzioni di riga fanno parte del valore. Un line.split(',') ingenuo in qualsiasi linguaggio rovinerà completamente tutti e tre. Ecco perché hai bisogno di un vero parser.

La regola d'oro del parsing CSV: Non dividere mai manualmente sulle virgole. Usa sempre una libreria di parsing CSV dedicata. Gestisce virgolette, sequenze di escape, campi multiriga e codifica — nessuna delle quali un semplice split può fare correttamente.

Varianti del Delimitatore: Tab, Punto e Virgola, Pipe

Nonostante il nome, CSV non deve necessariamente usare le virgole. Diverse varianti comuni usano delimitatori diversi, e le incontrerai tutte in natura:

  • TSV (Tab-Separated Values) — usa \t come delimitatore. Comune in bioinformatica (output BLAST, file VCF), esportazioni di database, e ovunque i valori stessi contengano frequentemente virgole
  • Separato da punto e virgola — il predefinito in Excel per le impostazioni locali dove la virgola è il separatore decimale (Germania, Francia, la maggior parte dell'UE). Se hai mai aperto un CSV da un collega europeo e hai ottenuto una gigantesca colonna, è questo il motivo
  • Separato da pipe — usa |. Comune nelle esportazioni dati bancari e assicurativi legacy dove i tab potrebbero essere rimossi dai sistemi mainframe
  • Larghezza fissa — tecnicamente non CSV, ma spesso raggruppato nella stessa categoria. Le colonne sono riempite a larghezze fisse invece di delimitare

La maggior parte dei parser CSV ti permette di specificare esplicitamente il delimitatore. Quando scrivi un CSV che altri consumeranno, documenta il tuo delimitatore. Quando ne leggi uno che non hai prodotto, controlla le prime righe prima di fare assunzioni. Il CSV Formatter può aiutarti a ispezionare e riformattare file con delimitatori non standard.

Parsing CSV in Python

La libreria standard di Python include il modulo csv, ed è genuinamente buono. Le due classi che userai di più sono csv.reader per l'accesso riga-come-lista e csv.DictReader per l'accesso riga-come-dizionario (che è quasi sempre quello che vuoi).

python
import csv

# csv.reader — each row is a list of strings
with open('orders.csv', 'r', encoding='utf-8', newline='') as f:
    reader = csv.reader(f)
    header = next(reader)          # consume the header row
    for row in reader:
        order_id, customer, product, quantity, price, shipped = row
        print(f"Order {order_id}: {quantity}x {product} for {customer}")

# csv.DictReader — each row is an OrderedDict keyed by the header
with open('orders.csv', 'r', encoding='utf-8', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        if row['shipped'] == 'false':
            print(f"Pending: {row['order_id']} — {row['customer']}")

Due cose da includere sempre: encoding='utf-8' (o qualunque sia la codifica effettiva del file), e newline=''. Quest'ultimo è critico — il modulo csv esegue la propria gestione delle nuove righe e ha bisogno dei byte grezzi. Senza newline='', puoi ottenere righe vuote extra su Windows.

La scrittura è altrettanto semplice con csv.writer:

python
import csv

orders = [
    {'order_id': 1008, 'customer': 'Grace Kim', 'product': 'Laptop Stand', 'quantity': 1, 'unit_price': 49.99, 'shipped': False},
    {'order_id': 1009, 'customer': 'Henry Park', 'product': 'USB-C Hub', 'quantity': 2, 'unit_price': 34.99, 'shipped': True},
]

with open('new_orders.csv', 'w', encoding='utf-8', newline='') as f:
    fieldnames = ['order_id', 'customer', 'product', 'quantity', 'unit_price', 'shipped']
    writer = csv.DictWriter(f, fieldnames=fieldnames)

    writer.writeheader()
    writer.writerows(orders)

# To use a different delimiter (e.g. tab-separated)
with open('new_orders.tsv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.writer(f, delimiter='\t')
    writer.writerow(['order_id', 'customer', 'product'])
    writer.writerow([1008, 'Grace Kim', 'Laptop Stand'])

Il modulo csv gestisce automaticamente tutte le virgolette — se un campo contiene una virgola o una nuova riga, lo avvolge in virgolette doppie senza che tu debba pensarci. Questo è l'intero punto dell'utilizzo di una libreria.

Parsing CSV in JavaScript / Node.js

JavaScript non ha un parser CSV integrato. La tentazione è di fare questo:

js
// ❌ Don't do this — breaks immediately on quoted fields
const rows = csvText.split('\n').map(line => line.split(','));

Questo fallisce nel momento in cui un valore contiene una virgola, una nuova riga all'interno delle virgolette, o una virgoletta doppia con escape. Per qualsiasi cosa reale, usa una libreria. PapaParse è la scelta principale — è veloce, gestisce tutti i casi limite, funziona sia nel browser che in Node.js, e supporta lo streaming per file di grandi dimensioni.

js
import Papa from 'papaparse';
import fs from 'fs';

// Parse a CSV string
const csvText = fs.readFileSync('orders.csv', 'utf8');

const result = Papa.parse(csvText, {
  header: true,          // first row becomes object keys
  skipEmptyLines: true,  // ignore blank lines at end of file
  dynamicTyping: true,   // converts "true"/"false" to booleans, numbers to numbers
});

console.log(result.data);
// [
//   { order_id: 1001, customer: 'Alice Nguyen', product: 'USB-C Hub', quantity: 2, unit_price: 34.99, shipped: true },
//   { order_id: 1002, customer: 'Bob Martinez', product: 'Mechanical Keyboard', quantity: 1, unit_price: 129, shipped: false },
//   ...
// ]

// Check for parse errors
if (result.errors.length > 0) {
  console.error('Parse errors:', result.errors);
}

// Generate CSV from an array of objects
const orders = [
  { order_id: 1008, customer: 'Grace Kim', product: 'Laptop Stand', quantity: 1, unit_price: 49.99 },
  { order_id: 1009, customer: 'Henry Park', product: 'USB-C Hub', quantity: 2, unit_price: 34.99 },
];

const csvOutput = Papa.unparse(orders);
fs.writeFileSync('export.csv', csvOutput, 'utf8');

L'opzione dynamicTyping è comoda ma vale la pena conoscerla — converte automaticamente cose come "34.99" in un numero. Di solito è quello che vuoi, ma può sorprenderti se un campo come order_id è un numero nel CSV ma lo volevi come stringa. Disattivalo se hai bisogno di output di stringa stretto.

Per conversioni rapide tra CSV e altri formati, lo strumento CSV to JSON gestisce i casi comuni più frequenti nel browser senza alcun codice — utile per trasformazioni dati una tantum. C'è anche CSV to XML se devi alimentare i dati a un sistema basato su XML.

Insidie Comuni che Incontrerai Sicuramente

Anche con un buon parser, CSV ha alcune mine ben note. Ecco quelle che si presentano più spesso:

  • Byte BOM da Excel. Quando Excel esporta un CSV come "UTF-8", spesso antepone un BOM UTF-8 (EF BB BF in esadecimale, o \ufeff come carattere). Questo fa sì che il primo nome della colonna dell'intestazione sembri order_id invece di order_id. In Python, apri il file con encoding='utf-8-sig' invece di utf-8 per rimuoverlo automaticamente. PapaParse lo gestisce in modo trasparente.
  • Terminazioni di riga CRLF vs LF. RFC 4180 specifica CRLF (\r\n), ma gli strumenti Unix producono LF (\n) e i vecchi file Mac usano solo CR (\r). Ecco perché il modulo csv di Python ha bisogno di newline='' — gestisce tutti e tre internamente. Se leggi byte grezzi e li dividi manualmente, devi rimuovere esplicitamente \r.
  • Problemi di codifica. CSV non ha modo di dichiarare la propria codifica — a differenza di <meta charset> di HTML o <?xml encoding="..."?> di XML. Excel spesso salva i file in Windows-1252 (a.k.a. CP1252) invece di UTF-8, il che storpia i caratteri accentati. Se vedi caratteri come é invece di é, hai un file UTF-8 decodificato come Latin-1, o viceversa. Stabilisci sempre la codifica fuori banda con chi produce il file.
  • Numeri che sembrano date. Excel converte silenziosamente valori come 1-2 o 03/04 in date all'apertura o al salvataggio. Se stai esportando codici prodotto o numeri di versione, anteponi una virgoletta singola in Excel ('1-2) per evitarlo — o di' a chi produce il file di farlo.
  • Virgole finali. Alcuni esportatori emettono una virgola finale alla fine di ogni riga, che crea una colonna vuota fantasma. Un parser robusto la ignora; una divisione ingenua crea un elemento stringa vuoto extra.

Se stai lavorando con un file che sembra strano, il CSV Validator può dirti rapidamente se il file è ben formato e segnalare problemi di codifica o strutturali prima che tu provi a elaborarlo.

CSV vs JSON vs Excel — Quando Usare Cosa

Questi tre formati si sovrappongono molto in pratica, ma ognuno ha un punto di forza chiaro:

  • Usa CSV quando stai spostando dati piatti e tabulari tra sistemi — esportazioni di database, pipeline di analisi, importazioni di fogli di calcolo, caricamento dati in blocco. È universalmente supportato, piccolo in dimensione e facilmente confrontabile con git. Il vincolo: è piatto. Nessuna nidificazione, nessun tipo, nessuna relazione.
  • Usa JSON quando i dati sono gerarchici o lo schema è importante. Un ordine con più voci, un file di configurazione con oggetti annidati, una risposta API — queste sono naturalmente JSON. CSV ti costringerebbe a denormalizzare i dati o a inventare la tua convenzione di nidificazione. La specifica JSON è pulita e non ambigua; il formato preserva i tipi (numeri, booleani, null, array, oggetti).
  • Usa Excel (.xlsx) quando l'output è per gli esseri umani, non per le macchine. Formattazione, formule, più fogli, grafici — se un utente aziendale è il consumatore finale, Excel è spesso la scelta giusta. Non usarlo mai come formato di interscambio tra sistemi. La specifica OOXML è enorme e il formato è fragile tra le versioni.

C'è un'euristica pratica: se guarderesti naturalmente i dati in un foglio di calcolo, CSV va probabilmente bene. Se li guarderesti in un albero o in un editor di codice, usa JSON. Se li stai inviando a un stakeholder aziendale che li filtrerà e ordinerà, usa Excel.

Conclusione

CSV è semplice per design e ingannevolmente difficile in pratica. Il formato non ha uno standard applicato, è disponibile in più varianti di delimitatore, e si basa su regole di virgolettatura che rompono ogni tentativo di parsing manuale. La soluzione è sempre la stessa: usa un parser reale (il modulo csv integrato di Python, o PapaParse in JavaScript), specifica sempre esplicitamente la codifica, e fai attenzione ai byte BOM di Excel. Una volta che hai un parser affidabile, CSV è in realtà un ottimo formato — veloce da produrre, facile da ispezionare in qualsiasi editor di testo, e supportato ovunque. Per il lavoro quotidiano con file CSV, gli strumenti CSV to JSON, CSV Formatter e CSV Validator gestiscono le operazioni comuni senza scrivere una riga di codice.