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.

  1. Validar las cadenas estar seguro de que ellos tienen x y y.
  2. En realidad, analizar los valores de x y y.
  3. 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.

¿Fue útil?

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í.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top