El análisis de atributos con expresiones regulares en Perl
Pregunta
He aquí un problema que me encontré recientemente.He atributos de las cadenas de la forma
"x=1 and y=abc and z=c4g and ..."
Algunos atributos tienen valores numéricos, algunos tienen valores de alfa, algunos se han mezclado, algunos han fechas, etc.
Cada cadena se supone para tener "x=someval and y=anotherval
"al principio, pero algunos no.Tengo tres cosas que necesita hacer.
- Validar las cadenas estar seguro de que ellos tienen
x
yy
. - En realidad, analizar los valores de
x
yy
. - El resto de la cadena.
Dado el ejemplo en la parte superior, este sería el resultado en las siguientes variables:
$x = 1;
$y = "abc";
$remainder = "z=c4g and ..."
Mi pregunta es:Hay un (razonablemente) de manera simple para analizar estos y validar con una sola expresión regular?es decir:
if ($str =~ /someexpression/)
{
$x = $1;
$y = $2;
$remainder = $3;
}
Tenga en cuenta que la cadena puede consistir en sólo x
y y
los atributos.Esta es una cadena válida.
Voy a publicar mi solución como una respuesta, pero no cumple mis sola-regex preferencia.
Solución
Yo no soy el mejor en expresiones regulares, pero esto parece bastante cercano a lo que estás buscando:
/x=(.+) and y=([^ ]+)( and (.*))?/
Excepto que se usa $1, $2 y $4.En uso:
my @strs = ("x=1 and y=abc and z=c4g and w=v4l",
"x=yes and y=no",
"z=nox and w=noy");
foreach (@strs) {
if ($_ =~ /x=(.+) and y=([^ ]+)( and (.*))?/) {
$x = $1;
$y = $2;
$remainder = $4;
print "x: $x; y: $y; remainder: $remainder\n";
} else {
print "Failed.\n";
}
}
Salida:
x: 1; y: abc; remainder: z=c4g and w=v4l
x: yes; y: no; remainder:
Failed.
Por supuesto, esto deja afuera a un montón de comprobación de errores, y no lo sé todo acerca de sus entradas, pero esto parece funcionar.
Otros consejos
Suponiendo que usted también quiere hacer algo con el otro nombre=valor de pares así es como yo lo haría ( con Perl versión 5.10 ):
use 5.10.0;
use strict;
use warnings;
my %hash;
while(
$string =~ m{
(?: ^ | \G ) # start of string or previous match
\s*
(?<key> \w+ ) # word characters
=
(?<value> \S+ ) # non spaces
\s* # get to the start of the next match
(?: and )?
}xgi
){
$hash{$+{key}} = $+{value};
}
# to make sure that x & y exist
die unless exists $hash{x} and exists $hash{y};
En mayores de Perls ( al menos Perl 5.6 );
use strict;
use warnings;
my %hash;
while(
$string =~ m{
(?: ^ | \G ) # start of string or previous match
\s*
( \w+ ) = ( \S+ )
\s* # get to the start of the next match
(?: and )?
}xgi
){
$hash{$1} = $2;
}
# to make sure that x & y exist
die unless exists $hash{x} and exists $hash{y};
Estos tienen la ventaja añadida de continuar el trabajo de si necesitas trabajar con más datos.
Como una manera bastante simple modificación Rudd versión,
/^x=(.+) and y=([^ ]+)(?: and (.*))?/
permitirá el uso de $1, $2 y $3 (el ?:hace un grupo que no captura), y se asegurará de que la cadena comienza con "x=" en lugar de permitir que un "not_x=" para que coincida con
Si usted tiene un mejor conocimiento de lo que los valores de x e y será, este debe ser utilizado para apretar el regex más:
my @strs = ("x=1 and y=abc and z=c4g and w=v4l",
"x=yes and y=no",
"z=nox and w=noy",
"not-x=nox and y=present",
"x=yes and w='there is no and y=something arg here'");
foreach (@strs) {
if ($_ =~ /^x=(.+) and y=([^ ]+)(?: and (.*))?/) {
$x = $1;
$y = $2;
$remainder = $3;
print "x: {$x}; y: {$y}; remainder: {$remainder}\n";
} else {
print "$_ Failed.\n";
}
}
Salida:
x: {1}; y: {abc}; remainder: {z=c4g and w=v4l}
x: {yes}; y: {no}; remainder: {}
z=nox and w=noy Failed.
not-x=nox and y=present Failed.
x: {yes and w='there is no}; y: {something}; remainder: {}
Tenga en cuenta que la parte que falta de la última prueba es debido a la versión actual de la y de la prueba que no requiere espacios, si la x prueba tenido la misma restricción que la cadena habría fracasado.
Rudd y Cebjyre han conseguido que la mayor parte del camino, pero ambos tienen ciertos problemas:
Rudd se sugiere:
/x=(.+) y y=([^ ]+)( y (.*))?/
Cebjyre modificado a:
/^x=(.+) y y=([^ ]+)(?:y (.*))?/
La segunda versión es mejor, porque no confundir "not_x=foo" con "x=foo", pero acepta las cosas como "x=foo z=bar y=baz" y establecer $1 = "foo z=bar", que es indeseable.
Esto es probablemente lo que usted está buscando:
/^x=(\w+) y y=(\w+)(?:y (.*))?/
Esto no permite que nada entre la x= y y= opciones, lugares y permite y opcional " y...", que estará en $3
Aquí básicamente lo que hice para solucionar esto:
($x_str, $y_str, $remainder) = split(/ and /, $str, 3);
if ($x_str !~ /x=(.*)/)
{
# error
}
$x = $1;
if ($y_str !~ /y=(.*)/)
{
# error
}
$y = $1;
He omitido algunos adicionales de validación y manejo de errores.Esta técnica funciona, pero no es tan concisa o bastante, como me hubiera gustado.Espero que alguien tenga una idea mejor para mí.