Tengo algunos datos de TSV
ID Name Email
1 test [email protected]
321 stan [email protected]
Me gustaría analizar esto en una lista de hashes
@entities[0]<Name> eq "test";
@entities[1]<Email> eq "[email protected]";
Tengo problemas para usar el metacarácter de nueva línea para delimitar la fila de encabezado de las filas de valor. Mi definición gramatical:
use v6;
grammar Parser {
token TOP { <headerRow><valueRow>+ }
token headerRow { [\s*<header>]+\n }
token header { \S+ }
token valueRow { [\s*<value>]+\n? }
token value { \S+ }
}
my $dat = q:to/EOF/;
ID Name Email
1 test [email protected]
321 stan [email protected]
EOF
say Parser.parse($dat);
Pero esto está volviendo Nil
. Creo que estoy malinterpretando algo fundamental sobre las expresiones regulares en raku.
Nil
. Es bastante árido en lo que respecta a los comentarios, ¿verdad? Para la depuración, descargue Commaide si aún no lo ha hecho, y / o vea ¿Cómo se pueden mejorar los informes de errores en las gramáticas? . TienesNil
porque tu patrón asumió una semántica de retroceso. Mira mi respuesta al respecto. Te recomiendo que evites el retroceso. Ver la respuesta de @ user0721090601 al respecto. Para mayor practicidad y velocidad, vea la respuesta de JJ. Además, la respuesta general introductoria a "Quiero analizar X con Raku. ¿Alguien puede ayudar?" .Respuestas:
Probablemente lo principal que lo está descartando es que
\s
coincide con el espacio horizontal y vertical. Para que coincida con el espacio justo horizontal, uso\h
, y para que coincida con el espacio justo vertical,\v
.Una pequeña recomendación que haría es evitar incluir las nuevas líneas en el token. También es posible que desee utilizar los operadores de alternancia
%
o%%
, como están diseñados para manejar este tipo de trabajo:El resultado de
Parser.parse($dat)
esto es el siguiente:lo que nos muestra que la gramática ha analizado todo con éxito. Sin embargo, centrémonos en la segunda parte de su pregunta, que desea que esté disponible en una variable para usted. Para hacer eso, deberá proporcionar una clase de acciones que sea muy simple para este proyecto. Simplemente crea una clase cuyos métodos coinciden con los métodos de su gramática (aunque se pueden ignorar los muy simples, como
value
/header
que no requieren un procesamiento especial además de la stringificación). Hay algunas formas más creativas / compactas de manejar el procesamiento de los suyos, pero seguiré con un enfoque bastante rudimentario para la ilustración. Aquí está nuestra clase:Cada método tiene la firma,
($/)
que es la variable de coincidencia de expresiones regulares. Ahora, preguntemos qué información queremos de cada token. En la fila del encabezado, queremos cada uno de los valores del encabezado, en una fila. Entonces:Cualquier señal con un cuantificador en ella será tratada como una
Positional
, por lo que también podría tener acceso a cada partido encabezado individuo con$<header>[0]
,$<header>[1]
, etc, pero esos son los objetos de los partidos, por lo que sólo stringify rápidamente. Elmake
comando permite que otros tokens accedan a estos datos especiales que hemos creado.Nuestra fila de valor se verá idénticamente, porque los
$<value>
tokens son lo que nos importa.Cuando lleguemos al último método, querremos crear la matriz con hashes.
Aquí puede ver cómo accedemos a las cosas que procesamos
headerRow()
yvalueRow()
: Usted usa el.made
método. Debido a que hay múltiples valueRows, para obtener cada uno de susmade
valores, necesitamos hacer un mapa (esta es una situación en la que tiendo a escribir mi gramática para tenerla simplemente<header><data>
en la gramática, y destruir los datos como filas múltiples, pero esto es lo suficientemente simple no es tan malo).Ahora que tenemos los encabezados y las filas en dos matrices, simplemente se trata de convertirlos en una matriz de hashes, lo que hacemos en el
for
bucle. Elflat @x Z @y
simplemente intercolates los elementos, y la asignación de hash hace lo que queremos decir, pero hay otras maneras de obtener la matriz de hash que desea.Una vez que haya terminado, solo
make
lo hará, y luego estará disponible enmade
el análisis:Es bastante común envolverlos en un método, como
De esa manera solo puedes decir
fuente
class Actions { has @!header; method headerRow ($/) { @!header = @<header>.map(~*); make @!header.List; }; method valueRow ($/) {make (@!header Z=> @<value>.map: ~*).Map}; method TOP ($/) { make @<valueRow>.map(*.made).List }
Por supuesto, primero deberías instanciarlo:actions(Actions.new)
.class Actions { has @!header; has %!entries … }
la creación de instancias, pero si crea una instancia, probablemente lo haga y solo tenga el valueRow agregue las entradas directamente para que termine con solomethod TOP ($!) { make %!entries }
. Pero esto es Raku después de todo y TIMTOWTDI :-)<valueRow>+ %% \n
(Capturar filas que están delimitadas por nuevas líneas), pero siguiendo esa lógica,<.ws>* %% <header>
sería "captura opcional espacios en blanco delimitados por espacios en blanco ". ¿Me estoy perdiendo de algo?<.ws>
no captura (lo<ws>
haría). El OP señaló que el formato TSV puede comenzar con un espacio en blanco opcional. En realidad, esto probablemente se definiría aún mejor con un token de espacio entre líneas definido como\h*\n\h*
, lo que permitiría que el valueRow se defina más lógicamente como<header> % <.ws>
%
/%%
llamado una operación de "alternancia" antes. Pero es el nombre correcto. (Considerando que el uso de la misma para|
,||
y primos siempre me ha parecido raro.). No había pensado en esta técnica "al revés" antes. Pero es un buen lenguaje para escribir expresiones regulares que coinciden con un patrón repetido con alguna afirmación de separador no solo entre coincidencias del patrón, sino que también lo permite en ambos extremos (usando%%
) o al principio pero no al final (usando%
), como, er, alternativa al final pero no iniciar la lógica derule
y:s
. Agradable. :)TL; DR: no lo haces. Simplemente use
Text::CSV
, que es capaz de manejar todos los formatos.Mostraré cuántos años
Text::CSV
probablemente será útil:La parte clave aquí es la mezcla de datos que convierte el archivo inicial en una matriz o matrices (en
@data
). Sin embargo, solo es necesario porque elcsv
comando no puede manejar cadenas; si los datos están en un archivo, está listo para comenzar.Se imprimirá la última línea:
El campo ID se convertirá en la clave del hash, y todo en una matriz de hash.
fuente
TL; DR
regex
s retroceso.token
s no. Es por eso que tu patrón no coincide. Esta respuesta se centra en explicar eso y cómo arreglar trivialmente su gramática. Sin embargo, probablemente debería reescribirlo, o usar un analizador existente, que es lo que definitivamente debe hacer si solo desea analizar TSV en lugar de aprender sobre expresiones regulares de raku.¿Un malentendido fundamental?
(Si ya sabe que el término "expresiones regulares" es muy ambiguo, considere omitir esta sección).
Una cosa fundamental que puede estar malentendiendo es el significado de la palabra "expresiones regulares". Aquí hay algunos significados populares que la gente asume:
Expresiones regulares formales.
Perl regexes.
Expresiones regulares compatibles con Perl (PCRE).
Expresiones de coincidencia de patrones de texto llamadas "expresiones regulares" que se parecen a cualquiera de las anteriores y hacen algo similar.
Ninguno de estos significados son compatibles entre sí.
Si bien las expresiones regulares de Perl son semánticamente un superconjunto de expresiones regulares formales, son mucho más útiles en muchos aspectos, pero también son más vulnerables a la regresión patológica .
Si bien las expresiones regulares compatibles con Perl son compatibles con Perl en el sentido de que originalmente eran las mismas que las expresiones regulares de Perl a fines de la década de 1990, y en el sentido de que Perl admite motores de expresiones regulares enchufables, incluido el motor PCRE, la sintaxis de expresión regular de PCRE no es idéntica al estándar Perl regex utilizado por defecto por Perl en 2020.
Y aunque las expresiones de coincidencia de patrones de texto llamadas "expresiones regulares" generalmente se parecen un poco entre sí y coinciden con todo el texto, hay docenas, quizás cientos, de variaciones en la sintaxis, e incluso en semántica para la misma sintaxis.
Las expresiones de coincidencia de patrones de texto Raku generalmente se denominan "reglas" o "expresiones regulares". El uso del término "expresiones regulares" transmite el hecho de que se parecen a otras expresiones regulares (aunque la sintaxis se ha limpiado). El término "reglas" transmite el hecho de que son parte de un conjunto mucho más amplio de características y herramientas que se amplían al análisis (y más allá).
La solución rápida
Con el aspecto fundamental anterior de la palabra "expresiones regulares" fuera del camino, ahora puedo pasar al aspecto fundamental del comportamiento de su "expresión regular" .
Si cambiamos tres de los patrones en su gramática para el
token
declarador alregex
declarador, su gramática funciona como usted pretendía:La única diferencia entre a
token
y aregex
es que aregex
retrocede mientrastoken
que a no. Así:Durante el procesamiento del último patrón (que podría llamarse "expresión regular", pero cuyo declarante real es
token
, noregex
),\S
se tragará'b'
, tal como lo hizo temporalmente durante el procesamiento de la expresión regular en la línea anterior. Pero, debido a que el patrón se declara como atoken
, el motor de reglas (también conocido como "motor de expresiones regulares") no retrocede , por lo que la coincidencia general falla.Eso es lo que está sucediendo en su OP.
La solución correcta
Una mejor solución en general es dejar de asumir un comportamiento de retroceso, porque puede ser lento e incluso catastróficamente lento (indistinguible del programa que se cuelga) cuando se usa para hacer coincidir una cadena construida maliciosamente o una con una combinación de caracteres accidentalmente desafortunada.
A veces los
regex
s son apropiados. Por ejemplo, si está escribiendo una única y una expresión regular hace el trabajo, entonces ya está. Esta bien. Esa es parte de la razón por la que la/ ... /
sintaxis en raku declara un patrón de retroceso, al igual queregex
. (De nuevo, puede escribir/ :r ... /
si desea activar el trinquete : "trinquete" significa lo contrario de "retroceso", por lo que:r
cambia una expresión regular atoken
semántica).Ocasionalmente, el retroceso todavía tiene un papel en un contexto de análisis. Por ejemplo, mientras que la gramática para raku generalmente evita el retroceso, y en cambio tiene cientos de
rule
sytoken
s, sin embargo, todavía tiene 3regex
s.He votado a favor de la respuesta de @ user0721090601 ++ porque es útil. También aborda varias cosas que inmediatamente me parecieron idiomáticamente fuera de su código, y, lo que es más importante, se adhiere a
token
s. Bien puede ser la respuesta que prefiera, que será genial.fuente