Un colega y yo hemos discutido recientemente si una expresión regular pura es capaz de encapsular completamente el formato csv, de modo que sea capaz de analizar todos los archivos con cualquier carácter de escape, carácter de comillas y carácter de separador.
La expresión regular no necesita ser capaz de cambiar estos caracteres después de la creación, pero no debe fallar en ningún otro caso de borde.
He argumentado que esto es imposible solo para un tokenizador. La única expresión regular que podría hacer esto es un estilo PCRE muy complejo que va más allá de la tokenización.
Estoy buscando algo en la línea de:
... el formato csv es una gramática libre de contexto y, como tal, es imposible analizar solo con expresiones regulares ...
¿O estoy equivocado? ¿Es posible analizar csv con solo una expresión regular POSIX?
Por ejemplo, si tanto el carácter de escape como el carácter de cotización son "
, entonces estas dos líneas son válidas csv:
"""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..."
fuente
"
? Entonces lo siguiente es válido:"""this is a test.""",""
Respuestas:
Agradable en teoría, terrible en la práctica.
Por CSV voy a asumir que te refieres a la convención como se describe en RFC 4180 .
Si bien la coincidencia de datos CSV básicos es trivial:
Nota: Por cierto, es mucho más eficiente usar una función .split ('/ n'). Split ('"') para datos muy simples y bien estructurados como este. Las expresiones regulares funcionan como un NDFSM (finito no determinista) State Machine) que pierde mucho tiempo retrocediendo una vez que comienza a agregar casos extremos como caracteres de escape.
Por ejemplo, aquí está la cadena de coincidencia de expresiones regulares más completa que he encontrado:
Maneja razonablemente valores de comillas simples y dobles, pero no líneas nuevas en valores, comillas escapadas, etc.
Fuente: Desbordamiento de pila: ¿cómo puedo analizar una cadena con JavaScript?
Se convierte en una pesadilla una vez que se introducen los casos límite comunes como ...
El caso de borde de nueva línea como valor solo es suficiente para romper el 99.9999% de los analizadores basados en RegEx encontrados en la naturaleza. La única alternativa "razonable" es utilizar la coincidencia RegEx para la tokenización básica de caracteres de control / sin control (es decir, terminal versus no terminal) combinada con una máquina de estado utilizada para análisis de nivel superior.
Fuente: Experiencia conocida también como dolor y sufrimiento extensos.
Soy el autor de jquery-CSV , el único analizador CSV basado en javascript, totalmente compatible con RFC en el mundo. He pasado meses abordando este problema, hablando con muchas personas inteligentes y probando un montón si diferentes implementaciones incluyen 3 reescrituras completas del motor del analizador principal.
tl; dr - Moraleja de la historia, PCRE solo apesta para analizar cualquier cosa menos las gramáticas regulares más simples y estrictas (es decir, Tipo III). Aunque, es útil para tokenizar cadenas terminales y no terminales.
fuente
Regex puede analizar cualquier lenguaje regular y no puede analizar cosas elegantes como las gramáticas recursivas. Pero CSV parece ser bastante regular, tan analizable con una expresión regular.
Trabajemos desde la definición : se permiten secuencias, alternativas de forma de elección (
|
) y repetición (estrella de Kleene, la*
).[^,]*
# cualquier carácter menos coma"([^\"]|\\\\|\\")*"
# secuencia de cualquier cosa menos comillas"
o comillas\"
escapadas o escapadas escapadas\\
("")*"
a la expresión anterior.|
<quoted-value>(,
<valor>)*
\n
también es obviamente regular.No probé meticulosamente cada una de estas expresiones, y nunca definí grupos de captura. También he pasado por alto algunos detalles técnicos, como las variantes de caracteres que se pueden utilizar en lugar de
,
,"
o la línea de separadores: éstas no se rompen la regularidad, que acaba de obtener varios idiomas ligeramente diferentes.Si puede detectar un problema en esta prueba, ¡por favor comente! :)
Pero a pesar de esto, el análisis práctico de archivos CSV mediante expresiones regulares puras puede ser problemático. Debe saber cuál de las variantes se está enviando al analizador, y no hay un estándar para ello. Puede probar varios analizadores en cada línea hasta que uno tenga éxito, o de alguna manera dividir el formato de los comentarios. Pero esto puede requerir medios distintos de las expresiones regulares para hacerlo de manera eficiente, o en absoluto.
fuente
[^,"]*|"(\\(\\|")|[^\\"])*"
, y el último debería ser algo así[^,"]*|"(""|[^"])*"
. (¡Cuidado, ya que no he probado ninguno de estos!)perl -pi -e 's/"([^\"]|\\\\|\\")*"/yay/'
y canalizo"I have here an item,\" that is a test\""
, el resultado es 'yay eso es una prueba \ "". Creo que tu expresión regular es defectuosa.Respuesta simple, probablemente no.
El primer problema es la falta de un estándar. Si bien se puede describir su csv de una manera estrictamente definida, no se puede esperar obtener archivos csv estrictamente definidos. "Sé conservador en lo que haces, sé liberal en lo que aceptas de los demás" -Jon Postal
Asumiendo que uno tiene un estándar estándar aceptable, está la cuestión de los caracteres de escape y si es necesario equilibrarlos.
Una cadena en muchos formatos csv se define como
string value 1,string value 2
. Sin embargo, si esa cadena contiene una coma, es ahora"string, value 1",string value 2
. Si contiene una cita se convierte en"string, ""value 1""",string value 2
.En este punto, creo que es imposible. El problema es que necesita determinar cuántas comillas ha leído y si una coma está dentro o fuera del modo de doble comilla del valor. El equilibrio entre paréntesis es un problema imposible de expresiones regulares. Algunos motores de expresiones regulares extendidas (PCRE) pueden manejarlo, pero entonces no es una expresión regular.
Puede encontrar /programming/8629763/csv-parsing-with-a-context-free-grammar útil.
Modificado:
He estado buscando formatos para caracteres de escape y no he encontrado ninguno que necesite un recuento arbitrario, por lo que probablemente ese no sea el problema.
Sin embargo, existen problemas sobre cuál es el carácter de escape y el delimitador de registro (para empezar). http://www.csvreader.com/csv_format.php es una buena lectura sobre los diferentes formatos en la naturaleza.
'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 (traducido)"This {0x1C}is a value"
La clave aquí es que es posible tener una cadena que siempre tendrá múltiples interpretaciones válidas.
La pregunta relacionada (para casos extremos) "¿es posible tener una cadena no válida que sea aceptada?"
Todavía dudo mucho que haya una expresión regular que pueda coincidir con cada CSV válido creado por alguna aplicación y rechazar cada csv que no se pueda analizar.
fuente
("")*"
. Si las cotizaciones dentro del valor están fuera de balance, ya no es asunto nuestro.Primero defina la gramática para su CSV (¿los delimitadores de campo se escapan o codifican de alguna manera si aparecen en el texto?) Y luego se puede determinar si es analizable con regex. Primero la gramática: segundo analizador: http://www.boyet.com/articles/csvparser.html Cabe señalar que este método usa un tokenizador, pero no puedo construir una expresión regular POSIX que coincida con todos los casos límite. Si su uso de formatos CSV no es regular y está libre de contexto ... entonces su respuesta está en su pregunta. Buena descripción general aquí: http://nikic.github.com/2012/06/15/The-true-power-of-regular-expressions.html
fuente
Esta expresión regular puede simular CSV normal, como se describe en el RFC:
/("(?:[^"]|"")*"|[^,"\n\r]*)(,|\r?\n|\r)/
Explicación:
("(?:[^"]|"")*"|[^,"\n\r]*)
- un campo CSV, citado o no"(?:[^"]|"")*"
- un campo citado;[^"]|""
- cada personaje no es"
, o"
escapó como""
[^,"\n\r]*
- un campo sin comillas, que puede no contener,
"
\n
\r
(,|\r?\n|\r)
- el siguiente separador, ya sea,
o una nueva línea\r?\n|\r
- una nueva línea, una de\r\n
\n
\r
Se puede hacer coincidir y validar un archivo CSV completo mediante el uso repetido de esta expresión regular. Luego es necesario arreglar los campos entre comillas y dividirlos en filas según los separadores.
Aquí hay un código para un analizador CSV en Javascript, basado en la expresión regular:
Si esta respuesta ayuda a resolver su argumento es para que usted decida; Estoy feliz de tener un analizador CSV pequeño, simple y correcto.
En mi opinión, un
lex
programa es más o menos una expresión regular grande, y esos pueden tokenizar formatos mucho más complejos, como el lenguaje de programación C.Con referencia a las definiciones RFC 4180 :
espacios se consideran parte de un campo y no se deben ignorar: está bien
El último campo del registro no debe ir seguido de una coma, no se aplica
La expresión regular en sí misma satisface la mayoría de los requisitos de RFC 4180. No estoy de acuerdo con los demás, pero es fácil ajustar el analizador para implementarlos.
fuente