Recentemente, um colega e eu discutimos se um regex puro é capaz de encapsular completamente o formato csv, de forma que ele é capaz de analisar todos os arquivos com qualquer caractere de escape, caractere de citação e caractere separador.
O regex não precisa ser capaz de alterar esses caracteres após a criação, mas não deve falhar em nenhum outro caso de borda.
Argumentei que isso é impossível apenas para um tokenizador. O único regex que pode fazer isso é um estilo PCRE muito complexo que vai além da tokenização.
Eu estou procurando algo ao longo das linhas de:
... o formato csv é uma gramática livre de contexto e, como tal, é impossível analisar apenas regex ...
Ou eu estou errado? É possível analisar o csv apenas com um regex POSIX?
Por exemplo, se o caractere de escape e o caractere de cotação são "
, essas duas linhas são csv válidas:
"""this is a test.""",""
"and he said,""What will be, will be."", to which I replied, ""Surely not!""","moving on to the next field here..."
fonte
"
. Então, o seguinte é válido:"""this is a test.""",""
Respostas:
Bom em teoria, terrível na prática
Por CSV , vou assumir que você quer dizer a convenção conforme descrito na RFC 4180 .
Embora a correspondência de dados CSV básicos seja trivial:
Nota: BTW, é muito mais eficiente usar uma função .split ('/ n'). Split ('"') para dados muito simples e bem estruturados como este. Expressões regulares funcionam como um NDFSM (finito não determinístico) State Machine) que desperdiça muito tempo retornando quando você começa a adicionar casos extremos, como caracteres de escape.
Por exemplo, aqui está a string de correspondência de expressão regular mais abrangente que eu encontrei:
Ele lida razoavelmente com valores entre aspas simples e duplas, mas não com novas linhas de valores, aspas escapadas, etc.
Origem: Estouro de Pilha - Como posso analisar uma string com JavaScript
Torna-se um pesadelo quando os casos comuns são apresentados como ...
Somente o caso de nova linha como valor é suficiente para quebrar 99,9999% dos analisadores baseados em RegEx encontrados na natureza. A única alternativa "razoável" é usar a correspondência RegEx para tokenização básica de caracteres de controle / não controle (ou seja, terminal x não terminal) emparelhada com uma máquina de estado usada para análises de nível superior.
Fonte: Experiência conhecida como dor e sofrimento extensos.
Sou o autor do jquery-CSV , o único analisador de CSV baseado em javascript e totalmente compatível com RFC do mundo. Passei meses enfrentando esse problema, conversando com muitas pessoas inteligentes e tentando várias implementações diferentes, incluindo três reescritas completas do mecanismo do analisador de núcleo.
tl; dr - Moral da história, somente o PCRE é péssimo por analisar qualquer coisa, exceto as gramáticas regulares mais simples e estritas (Ie Tipo III). Embora seja útil para tokenizar seqüências de caracteres terminais e não terminais.
fonte
O Regex pode analisar qualquer idioma comum e não pode analisar coisas sofisticadas, como gramáticas recursivas. Mas o CSV parece ser bastante regular, tão parseable com uma regex.
Vamos trabalhar a partir da definição : permitidas são sequência, alternativas de forma de escolha (
|
) e repetição (estrela de Kleene, the*
).[^,]*
# qualquer caractere, exceto vírgula"([^\"]|\\\\|\\")*"
# sequência de qualquer coisa, exceto aspas"
ou aspas\"
escapadas ou escapes escapadas\\
("")*"
à expressão acima.|
<quoted-value>(,
<valor>)*
\n
também é obviamente regular.Não testei meticulosamente cada uma dessas expressões e nunca defini grupos de captura. Eu também anotado sobre alguns aspectos técnicos, como as variantes de caracteres que podem ser utilizados em vez de
,
,"
ou linha de separadores: estes não quebrar a regularidade, você é só pegar várias línguas ligeiramente diferentes.Se você encontrar um problema nessa prova, comente! :)
Mas, apesar disso, a análise prática de arquivos CSV por expressões regulares puras pode ser problemática. Você precisa saber qual das variantes está sendo alimentada no analisador e não há um padrão para isso. Você pode tentar vários analisadores em cada linha até que um seja bem-sucedido ou, de alguma forma, adivinhar os comentários do formato. Mas isso pode exigir outros meios além das expressões regulares para fazer com eficiência, ou de todo.
fonte
[^,"]*|"(\\(\\|")|[^\\"])*"
, e o último deve ser algo como[^,"]*|"(""|[^"])*"
. (Cuidado, pois eu não testei um desses!)perl -pi -e 's/"([^\"]|\\\\|\\")*"/yay/'
e canalizá-lo"I have here an item,\" that is a test\""
, o resultado é `sim, isso é um teste \" ". Acho que seu regex é falho.Resposta simples - provavelmente não.
O primeiro problema é a falta de um padrão. Embora se possa descrever seu csv de maneira estritamente definida, não se pode esperar obter arquivos csv estritamente definidos. "Seja conservador no que faz, seja liberal no que aceita dos outros" - Jon Postal
Supondo que se tenha um padrão padrão aceitável, há a questão dos caracteres de escape e se esses precisam ser equilibrados.
Uma sequência em muitos formatos csv é definida como
string value 1,string value 2
. No entanto, se essa sequência contiver uma vírgula, será agora"string, value 1",string value 2
. Se ele contiver uma cotação, ele se tornará"string, ""value 1""",string value 2
.Neste ponto, acredito que é impossível. O problema é que você precisa determinar quantas aspas você leu e se uma vírgula está dentro ou fora do modo de aspas duplas do valor. Equilibrar parênteses é um problema impossível de regex. Alguns mecanismos de expressão regular estendida (PCRE) podem lidar com isso, mas não é uma expressão regular então.
Você pode achar /programming/8629763/csv-parsing-with-a-context-free-grammar útil.
Alteradas:
Eu tenho procurado formatos para caracteres de escape e não encontrei nenhum que precise de contagem arbitrária - então esse provavelmente não é o problema.
No entanto, existem questões sobre qual é o caractere de escape e o delimitador de registro (para começar). http://www.csvreader.com/csv_format.php é uma boa leitura sobre os diferentes formatos na natureza.
'This, is a value'
vs"This, is a value"
"This ""is a value"""
vs"This \"is a value\""
"This {rd}is a value"
vs (escapado)"This \{rd}is a value"
vs (traduzido)"This {0x1C}is a value"
O principal aqui é que é possível ter uma string que sempre terá várias interpretações válidas.
A pergunta relacionada (para casos extremos) "é possível ter uma sequência inválida aceita?"
Eu ainda duvido muito que exista uma expressão regular que possa corresponder a todos os CSV válidos criados por algum aplicativo e rejeitar todos os CSVs que não podem ser analisados.
fonte
("")*"
. Se as cotações dentro do valor estiverem desequilibradas, já não é da nossa conta.Primeiro defina a gramática do seu CSV (os delimitadores de campo são escapados ou codificados de alguma forma se aparecerem em texto?) E, em seguida, é possível determinar se é analisável com regex. Gramática primeiro: analisador segundo: http://www.boyet.com/articles/csvparser.html Deve-se observar que esse método usa um tokenizer - mas não consigo criar um regex POSIX que corresponda a todos os casos extremos. Se o uso de formatos CSV não for regular e sem contexto ... sua resposta estará na sua pergunta. Boa visão geral aqui: http://nikic.github.com/2012/06/15/The-true-power-of-regular-expressions.html
fonte
Este regexp pode tokenizar o CSV normal, conforme descrito no RFC:
/("(?:[^"]|"")*"|[^,"\n\r]*)(,|\r?\n|\r)/
Explicação:
("(?:[^"]|"")*"|[^,"\n\r]*)
- um campo CSV, citado ou não"(?:[^"]|"")*"
- um campo citado;[^"]|""
- cada personagem é ou não"
, ou"
escapou como""
[^,"\n\r]*
- um campo não citado, que não pode conter,
"
\n
\r
(,|\r?\n|\r)
- o seguinte separador,,
ou uma nova linha\r?\n|\r
- uma nova linha, uma das\r\n
\n
\r
Um arquivo CSV inteiro pode ser correspondido e validado usando esse regexp repetidamente. Em seguida, é necessário corrigir os campos entre aspas e dividi-los em linhas com base nos separadores.
Aqui está o código para um analisador CSV em Javascript, com base no regexp:
Se essa resposta ajuda a resolver seu argumento é para você decidir; Estou feliz por ter um analisador de CSV pequeno, simples e correto.
Na minha opinião, um
lex
programa é mais ou menos uma expressão regular grande, e esses podem simbolizar formatos muito mais complexos, como a linguagem de programação C.Com referência às definições da RFC 4180 :
espaços são considerados parte de um campo e não devem ser ignorados - ok
O último campo no registro não deve ser seguido por vírgula - não imposto
O próprio regexp satisfaz a maioria dos requisitos da RFC 4180. Não concordo com os outros, mas é fácil ajustar o analisador para implementá-los.
fonte