Convertire fra formati di elenco senza perdere dati: le regole di virgolettatura che nessuno legge
Pubblicato il 30/06/2025 · 13 min di lettura · Strumenti testo e lingua
Daniel Okonkwo — Sviluppatore front-end e redattore Tech presso Allin
Performance web · Formati di file
Verificato su 6 fonti
Trasformare un elenco a capo in un elenco a virgole è una riga di codice finché un elemento non contiene una virgola. Tre nomi — Rossi, Anna / Bianchi, Luca / D’Amico, Ugo — uniti con virgole e ritagliati danno sei elementi, non tre. Il rimedio è una regola di virgolettatura, e quella da seguire è la RFC 4180: un campo che contiene una virgola, una virgoletta doppia o un a capo deve essere racchiuso fra virgolette doppie, e una virgoletta doppia interna si maschera raddoppiandola. Scritto così, "Rossi, Anna","Bianchi, Luca","D’Amico, Ugo" si rilegge come esattamente tre elementi. Due conseguenze sorprendono. Un campo CSV può legalmente contenere un a capo: un file con due record può occupare tre righe fisiche, e tagliarlo sul carattere di fine riga è semplicemente sbagliato — i record sono separati da CRLF e solo un vero parser sa quali a capo contano. E una stringa vuota non è zero elementi: tagliare «» sulla virgola restituisce un elemento vuoto, mentre unire [] e unire [""] producono entrambi la stringa vuota, quindi le due liste diventano indistinguibili se non si virgolettano tutti i campi. Non convertire mai con un taglio ingenuo sul delimitatore.
Trasformare un elenco a capo in un elenco a virgole è banale finché un elemento non contiene una virgola. Le regole di virgolettatura della RFC 4180, perché un campo CSV può contenere un a capo, perché i fogli di calcolo europei usano il punto e virgola, e cosa fa un elemento vuoto all'andata e ritorno — ogni caso eseguito e stampato.
La riga di codice, e il momento esatto in cui si rompe
Elenco a capo verso elenco a virgole: è un'unione. Al contrario: un taglio. Entrambe sono corrette esattamente finché nessun elemento contiene il delimitatore, e appena uno lo contiene la conversione smette di essere reversibile senza dirlo. Il nostro elenco di prova da cinque elementi — Smith, John / Doe, Jane / O’Neill, «Bud» / un indirizzo su due righe / plain — unito con virgole e ritagliato ha restituito otto elementi. Nulla ha sollevato eccezioni, nulla ha avvisato, e tre degli otto erano frammenti di nomi. Quel silenzio è tutto il problema: un convertitore di elenchi che perde dati produce un elenco plausibile, non un errore.
Lo stesso esperimento in ciascuna delle nostre lingue dà la stessa forma: tre voci «cognome, nome» diventano sei dopo un'andata e ritorno ingenua, in inglese, francese, spagnolo, portoghese, tedesco e italiano. Scritte con un generatore RFC 4180 e rilette con un parser RFC 4180, tornano tutte come tre elementi identici all'input. La regola non è specifica di una lingua; lo sono solo i dati che la fanno inciampare.
Cosa dice davvero la RFC 4180
La RFC 4180 è breve, dell'ottobre 2005 e — utile saperlo — Informational anziché standard. Le sue sette regole: i record sono separati da CRLF; l'ultimo non deve terminarvi; una riga di intestazione facoltativa può venire per prima; i campi sono separati da virgole e gli spazi fanno parte del campo; virgolettare è facoltativo, ma un campo non virgolettato non può contenere una virgoletta doppia; i campi che contengono a capo, virgolette doppie o virgole devono essere racchiusi fra virgolette doppie; e una virgoletta doppia interna si maschera facendola precedere da un'altra virgoletta doppia. È l'ultima regola quella per cui si inventa un'alternativa, di solito una barra rovesciata, che nessun lettore CSV si aspetta.
La grammatica ABNF della sezione 2 è più severa di qualunque uso reale. TEXTDATA è definito come %x20-21 / %x23-2B / %x2D-7E, il che esclude la virgola a %x2C e la virgoletta doppia a %x22 — e anche ogni byte sopra 127. Abbiamo verificato: «plain» è conforme a TEXTDATA; «café», «naïve», «Straße» e «ação» no. In pratica il parametro charset del tipo di media text/csv porta la codifica e tutti scrivono UTF-8, ma è un promemoria che la RFC ha codificato un disordine esistente invece di progettare un formato. Il documento lo dice da sé, raccomandando di essere conservativi in ciò che si produce e liberali in ciò che si accetta.
Un campo CSV può contenere un a capo
È la regola che rompe più importatori, perché contraddice il modello mentale di un record per riga. Abbiamo scritto un file di due record il cui secondo campo è un indirizzo su due righe: i byte sono id-1,"Line one CRLF Line two",ok CRLF id-2,flat,ok. Tagliato sull'a capo dà tre frammenti; analizzato correttamente dà due record, il primo dei quali contiene l'a capo intatto. Qualunque codice che legga un CSV con readLines è sbagliato su questo input, e l'input non è esotico — indirizzi, descrizioni di prodotto e note incollate contengono a capo.
I fine riga meritano un paragrafo a sé. La RFC impone CRLF fra i record, e tagliare «a,b CRLF c,d CRLF» sul solo avanzamento riga restituisce ["a,b\r", "c,d\r", ""] — due campi con un ritorno a capo invisibile appiccicato e una coda vuota. Quel \r vagante è il motivo per cui un valore risulta diverso da sé stesso fra due sistemi, e per cui una stringa vuota finale diventa un'ultima riga fantasma. Un parser che consuma CRLF, LF e un CR isolato come separatori di record gestisce tutte e tre le famiglie di file e restituisce gli stessi due record per ciascuna.
Perché mezza Europa scrive il CSV con i punti e virgola
Chiedi alla piattaforma che aspetto ha un numero in ciascuna delle nostre sei locale e la collisione è evidente. Formattare 1234567.5 dà 1,234,567.5 in en-US, 1 234 567,5 in fr-FR con uno spazio stretto unificatore U+202F come separatore di gruppi, 1.234.567,5 in es-ES, de-DE e it-IT, e 1 234 567,5 in pt-PT con U+00A0. Cinque delle sei usano la virgola come segno decimale. Un listino delimitato da virgole in quelle locale contiene quindi una virgola dentro un campo in ogni riga — ed è esattamente per questo che i loro fogli di calcolo scrivono e si aspettano il punto e virgola.
L'esecuzione lo rende concreto. La riga Sedia / 1.299,00 / 2 unita con virgole viene letta come quattro campi — Sedia, 1.299, 00, 2 — perché il segno decimale in italiano è una virgola. La riga Chair / 1,299.00 / 2 viene letta anch'essa come quattro per la ragione speculare: il separatore delle migliaia in inglese è una virgola. Virgoletta il campo prezzo e tornano entrambe tre campi; usa un punto e virgola ed entrambe sono tre campi senza alcuna virgoletta. Nessuna via è più corretta: il punto e virgola è quella che un foglio di calcolo europeo apre senza finestra di importazione, le virgole virgolettate quella che accetterà un'API.
Elementi vuoti, separatori finali e ciò che nessuna andata e ritorno può recuperare
Tagliare la stringa vuota sulla virgola restituisce un elemento vuoto, non zero. «a,b,» restituisce tre elementi, l'ultimo vuoto. «,a» ne restituisce due, il primo vuoto. «a,,b» ne restituisce tre, quello di mezzo vuoto. Nessuno è un bug; discendono tutti da una sola definizione — un separatore separa, quindi n separatori significano n+1 elementi. Di solito si vuole la versione filtrata, e filtrare è una decisione che cancella in silenzio un campo davvero vuoto.
Il caso irrecuperabile sta dal lato della scrittura. Unire la lista vuota e unire una lista con una stringa vuota producono entrambe la stringa vuota, quindi le due sono identiche sul filo e nessun parser può distinguerle. Il nostro stesso parser minimale ha peggiorato le cose: leggendo la stringa vuota restituiva zero record, il che è giusto per uno dei due input e sbagliato per l'altro. Passare a uno scrittore che virgoletta ogni campo risolve esattamente questo: un elemento vuoto diventa i due caratteri "" e si rilegge come un singolo elemento vuoto, mentre zero elementi restano la stringa vuota e si rileggono come nulla. Se nei tuoi dati possono comparire elementi vuoti, virgolettare sempre non è una preferenza di stile.
JSON, tabulazioni e scegliere un delimitatore apposta
Un array JSON evita l'intera discussione virgolettando tutto e mascherando il resto: il nostro elenco da cinque elementi ha superato l'andata e ritorno immutato, con l'indirizzo su due righe salvato come un'unica stringa contenente \n. È il formato da scegliere quando c'è un programma a entrambi i capi. Il costo è che ogni consumatore deve essere un parser JSON, e che JSON ha tipi — un elenco di CAP torna come numeri se qualcuno li scrive senza virgolette, e 01234 torna come 1234 o come errore di sintassi.
I valori separati da tabulazioni sono il formato senza specifica, ed è per questo che funzionano così spesso e falliscono così in silenzio. Il nostro elenco avversario conteneva una tabulazione dentro un elemento, quindi un delimitatore di tabulazione l'avrebbe spezzato. Prima di scegliere un delimitatore, guarda: in quell'elenco virgola, punto e virgola e tabulazione comparivano dentro gli elementi, mentre la barra verticale, U+001F e il byte nullo no. Un delimitatore la cui assenza è dimostrata riporta la conversione alla riga di codice che sembrava all'inizio — e se nessuno è sicuro, virgoletta. Un'ultima nota pratica estranea al parsing: un campo che inizia con =, +, - o @ viene trattato come formula dai fogli di calcolo, quindi in un elenco di stringhe fornite dagli utenti quei campi vanno neutralizzati prima che qualcuno apra il file.
| Formato | Separatore di elementi | Separatore dentro un elemento | A capo dentro un elemento | Elementi dopo andata e ritorno |
|---|---|---|---|---|
| Un elemento per riga | Avanzamento riga | Nessun problema — le virgole sono caratteri ordinari | Impossibile — chiude l'elemento | 3 su 3 |
| Unione ingenua con virgole | Virgola, senza virgolette | Si rompe — l'elemento si spezza in due | Si rompe — sembra un nuovo record | 6 su 3 |
| CSV RFC 4180 | Virgola, campi virgolettati se serve | Virgolettare il campo | Legale dentro un campo virgolettato | 3 su 3 |
| CSV con punto e virgola (fogli di calcolo europei) | Punto e virgola | Stessa regola di virgolette, altro delimitatore | Legale dentro un campo virgolettato | 3 su 3 |
| Array JSON | Virgola fra stringhe virgolettate | Nessun problema — ogni stringa è virgolettata | Con escape \n | 3 su 3 |
| Separato da tabulazioni | Tabulazione | Sicuro solo se nessun elemento contiene una tabulazione — il nostro la conteneva | Non definito da alcuna norma | 3 su 3 solo con fortuna |
Domande frequenti
- Un campo CSV può davvero contenere un a capo?
- Sì, e la RFC 4180 lo dice esplicitamente: un campo che contiene a capo deve essere racchiuso fra virgolette doppie, e l'ABNF ammette CR e LF dentro un campo mascherato. Abbiamo scritto un file di due record il cui secondo campo conteneva un indirizzo su due righe; occupa tre righe fisiche, quindi contare le righe dà 3 e analizzare dà 2. Qualunque importatore basato sulla lettura di righe è sbagliato su quel file.
- Un file delimitato da punto e virgola è ancora CSV?
- Secondo la RFC 4180 no, perché la sua ABNF fissa il delimitatore a %x2C, la virgola. In pratica è ciò che scrivono i fogli di calcolo in ogni locale che usa la virgola decimale — cinque delle nostre sei. Tieni il resto delle regole: virgoletta i campi che contengono il delimitatore, raddoppia le virgolette, separa i record con CRLF. Nomina il file con onestà e dichiara il delimitatore quando lo consegni.
- Una stringa vuota è un elemento o zero?
- Il taglio dice uno: tagliare «» sulla virgola restituisce una lista con un elemento vuoto. L'unione non può dirlo, perché [] e [""] producono entrambe la stringa vuota. Quindi la risposta è una convenzione da scegliere e annotare, non qualcosa contenuto nei dati. L'unico modo di conservare la distinzione in un'andata e ritorno è virgolettare ogni campo, il che trasforma un elemento vuoto in due virgolette e zero elementi in nulla.
- Come mascherare una virgoletta doppia dentro un campo?
- Raddoppiandola, dentro un campo virgolettato. L'elemento say "hi" si scrive "say ""hi""" — una virgoletta di apertura, il testo con ogni virgoletta interna scritta due volte, una virgoletta di chiusura. La barra rovesciata non fa nulla: il CSV non ha escape con barra rovesciata. Il nostro elenco avversario da undici elementi, che comprendeva quella stringa e una già virgolettata, ha fatto l'andata e ritorno identico con questa regola.
- Quando conviene usare invece un array JSON?
- Ogni volta che a entrambi i capi ci sono programmi. JSON virgoletta ogni stringa e maschera i caratteri di controllo, quindi gli elementi con virgole, virgolette e a capo non richiedono trattamenti speciali: il nostro elenco da cinque elementi è tornato immutato, con l'elemento su due righe salvato con \n dentro un'unica stringa. Preferisci il CSV quando all'altro capo c'è un foglio di calcolo o una persona, e ricorda che JSON ha tipi: gli identificatori con zero iniziale devono restare stringhe.
Articoli che potrebbero interessarti
Tutte le guide →Strumenti correlati
Fonti
- IETF (RFC Editor) — RFC 4180 — Common Format and MIME Type for Comma-Separated Values (CSV) Files, October 2005, Informational
- IANA — Media type registration for text/csv, with the optional charset and header parameters
- W3C — Model for Tabular Data and Metadata on the Web — what a CSV file does and does not carry
- Ecma International — ECMA-404 — The JSON Data Interchange Syntax
- Unicode Consortium (CLDR) — Common Locale Data Repository — per-locale decimal and grouping separators
- OWASP — CSV Injection — why a field beginning with =, +, - or @ is a security concern in spreadsheets
Hai notato un errore in questo articolo?