Vai al contenuto
Allin

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

Daniel OkonkwoSviluppatore front-end e redattore Tech presso Allin

Performance web · Formati di file

Verificato su 6 fonti

Vedi il profilo
In breve

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.

Lo stesso elenco di tre elementi attraverso cinque formati, esecuzioni in Node 26.3.0. Elementi: Rossi, Anna / Bianchi, Luca / D’Amico, Ugo. Solo i formati con una regola di virgolettatura o di escape sopravvivono a un elemento che contiene il delimitatore.
FormatoSeparatore di elementiSeparatore dentro un elementoA capo dentro un elementoElementi dopo andata e ritorno
Un elemento per rigaAvanzamento rigaNessun problema — le virgole sono caratteri ordinariImpossibile — chiude l'elemento3 su 3
Unione ingenua con virgoleVirgola, senza virgoletteSi rompe — l'elemento si spezza in dueSi rompe — sembra un nuovo record6 su 3
CSV RFC 4180Virgola, campi virgolettati se serveVirgolettare il campoLegale dentro un campo virgolettato3 su 3
CSV con punto e virgola (fogli di calcolo europei)Punto e virgolaStessa regola di virgolette, altro delimitatoreLegale dentro un campo virgolettato3 su 3
Array JSONVirgola fra stringhe virgolettateNessun problema — ogni stringa è virgolettataCon escape \n3 su 3
Separato da tabulazioniTabulazioneSicuro solo se nessun elemento contiene una tabulazione — il nostro la contenevaNon definito da alcuna norma3 su 3 solo con fortuna
Convertitore formato elencoConverti un elenco tra punti elenco, numeri e righe semplici.Prova lo strumento

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
SpiegazioneDove una riga può andare a capo: l'algoritmo Unicode dietro ogni paragrafo mandato a capo«Andare a capo sugli spazi» fallisce nella maggior parte dei sistemi di scrittura. UAX #14 assegna a ogni carattere una classe di interruzione di riga; abbiamo cercato le nostre in Unicode 17.0.0 ed eseguito un'implementazione conforme su spazi unificatori, trattini morbidi, spazi a larghezza zero, URL, giapponese e thai.SpiegazioneFrequenza delle parole e legge di Zipf: abbiamo contato sei libri in sei lingue e stimato la pendenzaLa parola di rango n compare circa 1/n volte rispetto alla prima. Abbiamo contato sei libri di pubblico dominio, stampato rango × frequenza, stimato log frequenza contro log rango e ottenuto pendenze fra -1,02 e -1,08 in tutte e sei le lingue — più i due punti in cui la legge si rompe.GuidaLe liste di attività in Markdown e cosa viene reso davvero e doveLe liste di attività non sono in CommonMark. Sono un'estensione di GitHub Flavored Markdown, ed è per questo che lo stesso file mostra caselle in un posto e parentesi quadre letterali in un altro. La regola esatta del marcatore, cosa fa l'annidamento e una tabella di cosa è CommonMark, cosa è GFM e cosa non è nessuno dei due — verificato su entrambe le specifiche e quattro motori di resa.TutorialFiltrare righe secondo uno schema, senza riga di comandoQuesto è grep per chi non usa grep, con una differenza importante: la ricerca è una sottostringa letterale, quindi una vera espressione regolare restituisce un riquadro vuoto e nessun errore. Ogni affermazione è stata verificata eseguendo lo strumento.GuidaFormattare i numeri per sei lingue: separatori, valuta e il ritorno al valore1.234,56 e 1,234.56 sono lo stesso numero, e confonderli cambia il valore che un lettore legge. Abbiamo eseguito Intl.NumberFormat per tutte e sei le lingue del sito e stampato ogni separatore — compreso quello invisibile usato dal francese — e poi misurato perché parseFloat non può disfare nulla di tutto ciò.SpiegazioneLe emoji sono più difficili di quanto sembri: perché «basta toglierle» non ha una risposta in una rigaUna emoji visibile può valere un punto di codice o quattordici unità UTF-16. Abbiamo lanciato tre espressioni regolari diffuse su una frase vera e ognuna si è rotta in modo diverso; una ha cancellato le cifre. Ecco perché, quale proprietà Unicode risponde a quale domanda, e la regola a gruppi di grafemi che funziona davvero.

Strumenti correlati

Fonti

Hai notato un errore in questo articolo?