test - Regex Group en Perl: cómo capturar elementos en una matriz del grupo regex que coincide con el número desconocido de ocurrencias/múltiples/variables de una cadena?
regex one (9)
En Perl, ¿cómo puedo usar una agrupación regex para capturar más de una ocurrencia que coincida con ella, en varios elementos de la matriz?
Por ejemplo, para una cadena:
var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello
para procesar esto con código:
$string = "var1=100 var2=90 var5=hello var3=/"a, b, c/" var7=test var3=hello";
my @array = $string =~ <regular expression here>
for ( my $i = 0; $i < scalar( @array ); $i++ )
{
print $i.": ".$array[$i]."/n";
}
Me gustaría ver como salida:
0: var1=100
1: var2=90
2: var5=hello
3: var3="a, b, c"
4: var7=test
5: var3=hello
¿Qué usaría como expresión regular?
La coincidencia entre las cosas que quiero hacer coincidir aquí es un patrón de cadena de tareas, por lo que algo así como:
my @array = $string =~ m/(/w+=[/w/"/,/s]+)*/;
Donde * indica una o más ocurrencias que coinciden con el grupo.
(Desconocí el uso de una división () ya que algunas coincidencias contienen espacios dentro de ellas (es decir, var3 ...) y, por lo tanto, no daría los resultados deseados).
Con la expresión regular anterior, solo obtengo:
0: var1=100 var2
¿Es posible en una expresión regular? O se requiere un código de adición?
Miró ya las respuestas existentes, cuando buscaba "grupo múltiple perl regex" pero no suficientes pistas:
- Tratar con múltiples grupos de captura en múltiples registros
- Varias coincidencias dentro de un grupo regex?
- Regex: grupos de captura repetidos
- Emparejamiento y agrupación Regex
- ¿Cómo puedo emparejar la expresión regular con la agrupación con un número desconocido de grupos?
- awk extrae múltiples grupos de cada línea
- Coincidencia de múltiples grupos regex y eliminarlos
- Perl: Eliminar múltiples líneas de recu- brimiento donde se cumple un criterio determinado
- Regex haciendo coincidir en varios grupos por línea?
- RegEx de PHP Agrupación de múltiples coincidencias
- ¿Cómo encontrar múltiples ocurrencias con grupos regex?
Éste también le proporcionará escapes comunes entre comillas dobles, como por ejemplo var3 = "a, /" b, c ".
@a = /(/w+=(?:/w+|"(?:[^//"]*(?://.[^//"]*)*)*"))/g;
En acción:
echo ''var1=100 var2=90 var42="foo/"bar//" var5=hello var3="a, b, c" var7=test var3=hello'' |
perl -nle ''@a = /(/w+=(?:/w+|"(?:[^//"]*(?://.[^//"]*)*)*"))/g; $,=","; print @a''
var1=100,var2=90,var42="foo/"bar//",var5=hello,var3="a, b, c",var7=test,var3=hello
Con expresiones regulares, use una técnica que me gusta llamar táctica y estiramiento: ancle las características que sabe que estarán allí (viñeta) y luego agarre lo que está entre (estiramiento).
En este caso, sabes que una sola tarea coincide
/b/w+=.+
y tiene muchos de estos repetidos en $string
. Recuerde que /b
significa límite de palabras:
Un límite de palabras (
/b
) es un punto entre dos caracteres que tiene un/w
en un lado y un/W
en el otro lado del mismo (en cualquier orden), contando los caracteres imaginarios del principio y el final del cadena como coincidente con/W
Los valores en las asignaciones pueden ser un poco difíciles de describir con una expresión regular, pero también se sabe que cada valor terminará con espacios en blanco, aunque no necesariamente el primer espacio en blanco encontrado, seguido de otra asignación o final de cadena.
Para evitar repetir el patrón de afirmación, compilarlo una vez con qr//
y reutilizarlo en su patrón junto con una aserción de preanálisis (?=...)
para estirar la coincidencia lo suficiente como para capturar el valor completo y, a la vez, evitarlo de derramar en el siguiente nombre de variable.
Coincidencia con su patrón en el contexto de la lista con m//g
da el siguiente comportamiento:
El modificador
/g
especifica la coincidencia global de patrones, es decir, la coincidencia tantas veces como sea posible dentro de la cadena. Cómo se comporta depende del contexto. En el contexto de la lista, devuelve una lista de las subcadenas que coinciden con cualquier paréntesis de captura en la expresión regular. Si no hay paréntesis, devuelve una lista de todas las cadenas coincidentes, como si hubiera paréntesis alrededor de todo el patrón.
El patrón $assignment
usa no codicioso .+?
para cortar el valor tan pronto como la búsqueda anticipada vea otra asignación o final de línea. Recuerde que la coincidencia devuelve las subcadenas de todos los subpatrones de captura, por lo que la alternancia de la búsqueda anticipada utiliza la no captura (?:...)
. El qr//
, en cambio, contiene paréntesis de captura implícitos.
#! /usr/bin/perl
use warnings;
use strict;
my $string = <<''EOF'';
var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello
EOF
my $assignment = qr//b/w+ = .+?/x;
my @array = $string =~ /$assignment (?= /s+ (?: $ | $assignment))/gx;
for ( my $i = 0; $i < scalar( @array ); $i++ )
{
print $i.": ".$array[$i]."/n";
}
Salida:
0: var1=100 1: var2=90 2: var5=hello 3: var3="a, b, c" 4: var7=test 5: var3=hello
Es posible hacer esto con expresiones regulares, sin embargo es frágil.
my $string = "var1=100 var2=90 var5=hello var3=/"a, b, c/" var7=test var3=hello";
my $regexp = qr/( (?:/w+=[/w/,]+) | (?:/w+=/"[^/"]*/") )/x;
my @matches = $string =~ /$regexp/g;
No digo que esto es lo que debes hacer, pero lo que intentas hacer es escribir una Gramática . Ahora su ejemplo es muy simple para una Gramática, pero el módulo Regexp::Grammars Damian Conway es realmente genial en esto. Si tienes que hacer crecer esto, descubrirás que te hará la vida mucho más fácil. Lo uso bastante aquí, es un poco perl6-ish.
use Regexp::Grammars;
use Data::Dumper;
use strict;
use warnings;
my $parser = qr{
<[pair]>+
<rule: pair> <key>=(?:"<list>"|<value=literal>)
<token: key> var/d+
<rule: list> <[MATCH=literal]> ** (,)
<token: literal> /S+
}xms;
q[var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello] =~ $parser;
die Dumper {%/};
Salida:
$VAR1 = {
'''' => ''var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello'',
''pair'' => [
{
'''' => ''var1=100'',
''value'' => ''100'',
''key'' => ''var1''
},
{
'''' => ''var2=90'',
''value'' => ''90'',
''key'' => ''var2''
},
{
'''' => ''var5=hello'',
''value'' => ''hello'',
''key'' => ''var5''
},
{
'''' => ''var3="a, b, c"'',
''key'' => ''var3'',
''list'' => [
''a'',
''b'',
''c''
]
},
{
'''' => ''var7=test'',
''value'' => ''test'',
''key'' => ''var7''
},
{
'''' => ''var3=hello'',
''value'' => ''hello'',
''key'' => ''var3''
}
]
Recientemente tuve que analizar las líneas de "Asunto" de los certificados x509. Tenían una forma similar a la que usted proporcionó:
echo ''Subject: C=HU, L=Budapest, O=Microsec Ltd., CN=Microsec e-Szigno Root CA 2009/[email protected]'' | /
perl -wne ''my @a = m/(/w+/=.+?)(?=(?:, /w+/=|$))/g; print "$_/n" foreach @a;''
C=HU
L=Budapest
O=Microsec Ltd.
CN=Microsec e-Szigno Root CA 2009/[email protected]
Breve descripción de la expresión regular:
(/w+/=.+?)
- captura palabras seguidas de ''='' y cualquier símbolo subsiguiente en modo no codicioso
(?=(?:, /w+/=|$))
- que son seguidos por otro , KEY=val
o fin de línea.
La parte interesante de la expresión regular utilizada es:
-
.+?
- Modo no codicioso -
(?:pattern)
- modo de no captura -
(?=pattern)
Aserción positiva de previsión de ancho cero
Un poco exagerado, pero una excusa para que investigue http://p3rl.org/Parse::RecDescent . ¿Qué hay de hacer un analizador?
#!/usr/bin/perl
use strict;
use warnings;
use Parse::RecDescent;
use Regexp::Common;
my $grammar = <<''_EOGRAMMAR_''
INTEGER: /[-+]?/d+/
STRING: //S+/
QSTRING: /$Regexp::Common::RE{quoted}/
VARIABLE: /var/d+/
VALUE: ( QSTRING | STRING | INTEGER )
assignment: VARIABLE "=" VALUE /[/s]*/ { print "$item{VARIABLE} => $item{VALUE}/n"; }
startrule: assignment(s)
_EOGRAMMAR_
;
$Parse::RecDescent::skip = '''';
my $parser = Parse::RecDescent->new($grammar);
my $code = q{var1=100 var2=90 var5=hello var3="a, b, c" var7=test var8=" haha /" heh " var3=hello};
$parser->startrule($code);
rendimientos:
var1 => 100
var2 => 90
var5 => hello
var3 => "a, b, c"
var7 => test
var8 => " haha /" heh "
var3 => hello
PD. Tenga en cuenta el doble var3, si desea que la última asignación sobrescriba la primera, puede usar un hash para almacenar los valores y luego usarlos más tarde.
PPS. Lo primero que pensé fue dividir en ''='', pero eso fallaría si una cadena contuviera ''='' y como las expresiones regulares son casi siempre malas para el análisis, bueno, terminé intentándolo y funciona.
Editar: se agregó soporte para las comillas escapadas dentro de las cadenas entre comillas.
Usted solicitó una solución RegEx u otro código. Aquí hay una solución (principalmente) no regex que usa solo módulos centrales. La única expresión regular es /s+
para determinar el delimitador; en este caso, uno o más espacios.
use strict; use warnings;
use Text::ParseWords;
my $string="var1=100 var2=90 var5=hello var3=/"a, b, c/" var7=test var3=hello";
my @array = quotewords(''/s+'', 0, $string);
for ( my $i = 0; $i < scalar( @array ); $i++ )
{
print $i.": ".$array[$i]."/n";
}
O puede ejecutar el código HERE
El resultado es:
0: var1=100
1: var2=90
2: var5=hello
3: var3=a, b, c
4: var7=test
5: var3=hello
Si realmente quieres una solución de expresiones regulares, ¡el ideone.com/Qvm2u Alan Moore al vincular su código en IDEone es el gas!
#!/usr/bin/perl
use strict; use warnings;
use Text::ParseWords;
use YAML;
my $string =
"var1=100 var2=90 var5=hello var3=/"a, b, c/" var7=test var3=hello";
my @parts = shellwords $string;
print Dump /@parts;
@parts = map { { split /=/ } } @parts;
print Dump /@parts;
my $string = "var1=100 var2=90 var5=hello var3=/"a, b, c/" var7=test var3=hello";
while($string =~ /(?:^|/s+)(/S+)/s*=/s*("[^"]*"|/S*)/g) {
print "<$1> => <$2>/n";
}
Huellas dactilares:
<var1> => <100>
<var2> => <90>
<var5> => <hello>
<var3> => <"a, b, c">
<var7> => <test>
<var3> => <hello>
Explicación:
Última pieza primero: la bandera g
al final significa que puede aplicar la expresión regular a la cadena varias veces. La segunda vez continuará coincidiendo donde terminó el último partido en la cadena.
Ahora, para la expresión regular: (?:^|/s+)
coincide con el comienzo de la cadena o con un grupo de uno o más espacios. Esto es necesario, así que cuando la expresión regular se aplica la próxima vez, omitiremos los espacios entre los pares clave / valor. El ?:
Significa que el contenido entre paréntesis no se capturará como un grupo (no necesitamos los espacios, solo la clave y el valor). /S+
coincide con el nombre de la variable. Luego omitimos cualquier cantidad de espacios y un signo igual en el medio. Finalmente, ("[^"]*"|/S*)/
coincidir dos citas con cualquier cantidad de caracteres intermedios, o cualquier cantidad de caracteres que no sean espacios para el valor. Tenga en cuenta que la coincidencia de citas es bastante frágil y ganó maneje las citas escapadas correctamente, por ejemplo, "/"quoted/""
daría como resultado "/"
.
EDITAR:
Como realmente desea obtener toda la tarea, y no las llaves / valores individuales, aquí hay una sola línea que los extrae:
my @list = $string =~ /(?:^|/s+)((?:/S+)/s*=/s*(?:"[^"]*"|/S*))/g;