Converter entre formatos de lista sem perder dados: as regras de aspas que ninguém lê
Publicado a 30/06/2025 · 14 min de leitura · Ferramentas de texto e idioma
Daniel Okonkwo — Programador front-end e redator de Tecnologia na OneKitly
Desempenho web · Formatos de ficheiro
Verificado a partir de 6 fontes
Passar de uma lista com quebras de linha para uma lista com vírgulas é uma linha de código até um item conter uma vírgula. Três nomes — Silva, Ana / Costa, João / D’Ávila, Rui — unidos por vírgulas e voltados a cortar dão seis itens, não três. O remédio é uma regra de aspas, e a que se deve seguir é a RFC 4180: um campo que contenha uma vírgula, uma aspa dupla ou uma quebra de linha tem de ir entre aspas duplas, e uma aspa dupla dentro de um campo entre aspas escapa-se duplicando-a. Escrito assim, "Silva, Ana","Costa, João","D’Ávila, Rui" volta a ler-se como exatamente três itens. Duas consequências surpreendem. Um campo CSV pode legalmente conter uma quebra de linha: um ficheiro com dois registos pode ocupar três linhas físicas, e cortá-lo pelo caráter de fim de linha é simplesmente errado — os registos separam-se por CRLF e só um analisador a sério sabe que quebras contam. E uma cadeia vazia não é zero itens: cortar «» pela vírgula devolve um item vazio, enquanto juntar [] e juntar [""] produzem ambos a cadeia vazia, pelo que essas duas listas ficam indistinguíveis se não se puserem todos os campos entre aspas. Nunca converta com um corte ingénuo pelo delimitador.
Passar de uma lista com quebras de linha para uma lista com vírgulas é trivial até um item conter uma vírgula. As regras de aspas da RFC 4180, porque um campo CSV pode conter uma quebra de linha, porque as folhas de cálculo europeias usam o ponto e vírgula, e o que um item vazio faz à ida e volta — cada caso executado e impresso.
A linha de código e o momento exato em que quebra
Lista com quebras de linha para lista com vírgulas: é uma junção. Ao contrário: um corte. Ambas são corretas exatamente enquanto nenhum item contiver o delimitador, e assim que um contém, a conversão deixa de ser reversível sem o dizer. A nossa lista de teste de cinco itens — Smith, John / Doe, Jane / O’Neill, «Bud» / uma morada de duas linhas / plain — unida por vírgulas e voltada a cortar devolveu oito itens. Nada lançou exceção, nada avisou, e três dos oito eram fragmentos de nomes. Esse silêncio é o problema todo: um conversor de listas que perde dados produz uma lista plausível, não um erro.
A mesma experiência em cada uma das nossas línguas dá a mesma forma: três entradas «apelido, nome» tornam-se seis depois de uma ida e volta ingénua, em inglês, francês, espanhol, português, alemão e italiano. Escritas por um gerador RFC 4180 e relidas por um analisador RFC 4180, todas voltam como três itens idênticos à entrada. A regra não é própria de uma língua; só os dados que a fazem tropeçar o são.
O que a RFC 4180 diz mesmo
A RFC 4180 é curta, de outubro de 2005 e — convém saber — Informational, não uma norma. As suas sete regras: os registos separam-se por CRLF; o último não precisa de terminar com um; pode haver uma linha de cabeçalho opcional no início; os campos separam-se por vírgulas e os espaços fazem parte do campo; usar aspas é opcional, mas um campo sem aspas não pode conter uma aspa dupla; os campos que contêm quebras de linha, aspas duplas ou vírgulas devem ir entre aspas duplas; e uma aspa dupla dentro de um campo entre aspas escapa-se antecedendo-a de outra aspa dupla. Essa última regra é a que as pessoas substituem por uma invenção, normalmente uma barra invertida, que nenhum leitor de CSV espera.
A gramática ABNF da secção 2 é mais estrita do que qualquer uso real. TEXTDATA é definido como %x20-21 / %x23-2B / %x2D-7E, o que exclui a vírgula em %x2C e a aspa dupla em %x22 — e também todo o byte acima de 127. Verificámos: «plain» é conforme a TEXTDATA; «café», «naïve», «Straße» e «ação» não são. Na prática o parâmetro charset do tipo de média text/csv leva a codificação e toda a gente escreve UTF-8, mas é um lembrete de que a RFC codificou uma desordem existente em vez de desenhar um formato. O próprio documento o diz, recomendando ser conservador no que se produz e liberal no que se aceita.
Um campo CSV pode conter uma quebra de linha
É a regra que quebra mais importadores, porque contradiz o modelo mental de um registo por linha. Escrevemos um ficheiro de dois registos cujo segundo campo é uma morada de duas linhas: os bytes são id-1,"Line one CRLF Line two",ok CRLF id-2,flat,ok. Cortado pela mudança de linha dá três fragmentos; analisado corretamente dá dois registos, o primeiro dos quais contém a quebra intacta. Qualquer código que leia um CSV com readLines está errado nesta entrada, e a entrada não é exótica — moradas, descrições de produto e notas coladas contêm quebras de linha.
Os fins de linha merecem o seu parágrafo. A RFC exige CRLF entre registos, e cortar «a,b CRLF c,d CRLF» só pela mudança de linha devolve ["a,b\r", "c,d\r", ""] — dois campos com um retorno de carro invisível colado e uma cauda vazia. Esse \r solto explica que um valor seja comparado como diferente de si próprio entre dois sistemas e que uma cadeia vazia final se torne uma última linha fantasma. Um analisador que consuma CRLF, LF e um CR isolado como separadores de registo lida com as três famílias de ficheiros e devolve os mesmos dois registos para cada uma.
Porque metade da Europa escreve CSV com ponto e vírgula
Pergunte à plataforma que aspeto tem um número em cada uma das nossas seis locales e a colisão salta à vista. Formatar 1234567.5 dá 1,234,567.5 em en-US, 1 234 567,5 em fr-FR com um espaço fino inquebrável U+202F como separador de grupos, 1.234.567,5 em es-ES, de-DE e it-IT, e 1 234 567,5 em pt-PT com U+00A0. Cinco das seis usam a vírgula como marca decimal. Uma lista de preços delimitada por vírgulas nessas locales tem portanto uma vírgula dentro de um campo em cada linha — e é exatamente por isso que as suas folhas de cálculo escrevem e esperam o ponto e vírgula.
A execução torna-o concreto. A linha Cadeira / 1 299,00 / 2 unida por vírgulas analisa-se como quatro campos — Cadeira, 1 299, 00, 2 — porque a marca decimal é uma vírgula em português. A linha Chair / 1,299.00 / 2 analisa-se também como quatro pela razão espelhada: o separador de milhares é uma vírgula em inglês. Ponha o campo do preço entre aspas e ambas voltam a ser três campos; use um ponto e vírgula e ambas são três campos sem aspa nenhuma. Nenhuma via é mais correta: o ponto e vírgula é a que uma folha de cálculo europeia abre sem caixa de diálogo de importação, e as vírgulas entre aspas a que uma API aceitará.
Itens vazios, separadores finais e o que nenhuma ida e volta pode recuperar
Cortar a cadeia vazia pela vírgula devolve um item vazio, não zero. «a,b,» devolve três itens, o último vazio. «,a» devolve dois, o primeiro vazio. «a,,b» devolve três, o do meio vazio. Nenhum é um erro; todos decorrem de uma só definição — um separador separa, portanto n separadores significam n+1 itens. O que se costuma querer é a versão filtrada, e filtrar é uma decisão que apaga em silêncio um campo genuinamente vazio.
O caso irrecuperável está no lado da escrita. Juntar a lista vazia e juntar uma lista com uma cadeia vazia produzem ambas a cadeia vazia, portanto as duas são idênticas no fio e nenhum analisador as distingue. O nosso próprio analisador mínimo agravou-o: ao ler a cadeia vazia devolvia zero registos, o que é certo para uma das duas entradas e errado para a outra. Passar a um gerador que põe todos os campos entre aspas corrige exatamente isso: um item vazio passa a ser os dois carateres "" e relê-se como um único item vazio, enquanto zero itens continuam a cadeia vazia e releem-se como nada. Se podem ocorrer itens vazios nos seus dados, citar sempre não é uma questão de estilo.
JSON, tabulações e escolher um delimitador de propósito
Um array JSON contorna toda a discussão pondo tudo entre aspas e escapando o resto: a nossa lista de cinco itens sobreviveu à ida e volta sem mudanças, com a morada de duas linhas guardada como uma só cadeia contendo \n. É o formato a escolher quando há um programa nas duas pontas. O seu custo é que todo o consumidor tem de ser um analisador JSON, e que JSON tem tipos — uma lista de códigos postais volta como números se alguém os escrever sem aspas, e 01234 volta como 1234 ou como erro de sintaxe.
Os valores separados por tabulações são o formato sem especificação, e é por isso que funcionam tantas vezes e falham tão calados. A nossa lista adversa continha uma tabulação dentro de um item, portanto um delimitador de tabulação tê-lo-ia partido. Antes de escolher delimitador, olhe: nessa lista a vírgula, o ponto e vírgula e a tabulação apareciam dentro de itens, enquanto a barra vertical, U+001F e o byte nulo não. Um delimitador que comprovadamente não ocorre devolve a conversão à linha de código que parecia ao início — e se nenhum for seguro, use aspas. Uma última nota prática alheia à análise: um campo que começa por =, +, - ou @ é tratado como fórmula por uma folha de cálculo, portanto uma lista de cadeias fornecidas por utilizadores deve ter esses campos neutralizados antes de alguém abrir o ficheiro.
| Formato | Separador de itens | Separador dentro de um item | Quebra de linha dentro de um item | Itens depois da ida e volta |
|---|---|---|---|---|
| Um item por linha | Mudança de linha | Sem problema — as vírgulas são carateres vulgares | Impossível — termina o item | 3 de 3 |
| Junção ingénua por vírgulas | Vírgula, sem aspas | Quebra — o item parte-se em dois | Quebra — parece um registo novo | 6 de 3 |
| CSV RFC 4180 | Vírgula, campos entre aspas quando preciso | Pôr o campo entre aspas | Legal dentro de um campo entre aspas | 3 de 3 |
| CSV com ponto e vírgula (folhas de cálculo europeias) | Ponto e vírgula | Mesma regra de aspas, outro delimitador | Legal dentro de um campo entre aspas | 3 de 3 |
| Array JSON | Vírgula entre cadeias entre aspas | Sem problema — toda a cadeia vai entre aspas | Escapado como \n | 3 de 3 |
| Separado por tabulações | Tabulação | Seguro só se nenhum item contiver uma tabulação — o nosso continha | Não definido por nenhuma norma | 3 de 3 só com sorte |
Perguntas frequentes
- Um campo CSV pode mesmo conter uma quebra de linha?
- Sim, e a RFC 4180 di-lo explicitamente: um campo que contenha quebras de linha deve ir entre aspas duplas, e a ABNF permite CR e LF dentro de um campo escapado. Escrevemos um ficheiro de dois registos cujo segundo campo tinha uma morada de duas linhas; ocupa três linhas físicas, portanto contar linhas dá 3 e analisar dá 2. Qualquer importador construído sobre a leitura de linhas está errado nesse ficheiro.
- Um ficheiro delimitado por ponto e vírgula ainda é CSV?
- Segundo a RFC 4180 não, porque a sua ABNF fixa o delimitador em %x2C, a vírgula. Na prática é o que as folhas de cálculo escrevem em qualquer locale que use a vírgula decimal — cinco das nossas seis. Mantenha o resto das regras: use aspas nos campos que contenham o delimitador, duplique as aspas, separe os registos com CRLF. Nomeie o ficheiro com honestidade e indique o delimitador ao entregá-lo.
- Uma cadeia vazia é um item ou zero?
- O corte diz um: cortar «» pela vírgula devolve uma lista com um item vazio. A junção não o pode dizer, porque [] e [""] produzem ambas a cadeia vazia. Portanto a resposta é uma convenção que tem de escolher e registar, não algo que os dados contenham. A única forma de manter a distinção numa ida e volta é pôr todos os campos entre aspas, o que transforma um item vazio em duas aspas e zero itens em nada.
- Como escapo uma aspa dupla dentro de um campo?
- Duplicando-a, dentro de um campo entre aspas. O item say "hi" escreve-se "say ""hi""" — uma aspa de abertura, o texto com cada aspa interior escrita duas vezes, uma aspa de fecho. A barra invertida não faz nada: o CSV não tem escape por barra invertida. A nossa lista adversa de onze itens, que incluía essa cadeia e outra já entre aspas, fez a ida e volta idêntica com esta regra.
- Quando devo antes usar um array JSON?
- Sempre que ambas as pontas sejam programas. O JSON põe cada cadeia entre aspas e escapa os carateres de controlo, portanto itens com vírgulas, aspas e quebras de linha não precisam de tratamento especial: a nossa lista de cinco itens fez a ida e volta sem mudanças, com o item de duas linhas guardado com \n dentro de uma só cadeia. Prefira CSV quando do outro lado estiver uma folha de cálculo ou uma pessoa, e lembre-se de que o JSON tem tipos: identificadores com zero inicial devem continuar cadeias.
Artigos que podem interessar-lhe
Todos os guias →Ferramentas relacionadas
Fontes
- 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
Detetaste um erro neste artigo?