Question

Voici un problème que j'ai rencontré récemment.J'ai des chaînes d'attributs du formulaire

"x=1 and y=abc and z=c4g and ..."

Certains attributs ont des valeurs numériques, certains ont des valeurs alpha, certains sont mixtes, certains ont des dates, etc.

Chaque chaîne est censé avoir "x=someval and y=anotherval" au début, mais certains ne le font pas.J'ai trois choses à faire.

  1. Validez les chaînes pour être certain qu'elles ont x et y.
  2. Analysez en fait les valeurs de x et y.
  3. Obtenez le reste de la chaîne.

Compte tenu de l'exemple ci-dessus, cela donnerait les variables suivantes :

$x = 1;
$y = "abc";
$remainder = "z=c4g and ..."

Ma question est:Existe-t-il un moyen (raisonnablement) simple d'analyser ces et valider avec une seule expression régulière ?c'est à dire.:

if ($str =~ /someexpression/)
{
    $x = $1;
    $y = $2;
    $remainder = $3;
}

Notez que la chaîne peut être composée de seulement x et y les attributs.Il s'agit d'une chaîne valide.

Je publierai ma solution comme réponse, mais elle ne répond pas à ma préférence d'expression régulière unique.

Était-ce utile?

La solution

Je ne suis pas le meilleur en expressions régulières, mais cela semble assez proche de ce que vous recherchez :

/x=(.+) and y=([^ ]+)( and (.*))?/

Sauf que vous utilisez 1 $, 2 $ et 4 $.Utilisé:

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";
    }
}

Sortir:

x: 1; y: abc; remainder: z=c4g and w=v4l
x: yes; y: no; remainder: 
Failed.

Bien sûr, cela laisse de côté de nombreuses vérifications d'erreurs, et je ne sais pas tout de vos entrées, mais cela semble fonctionner.

Autres conseils

En supposant que vous vouliez également faire quelque chose avec les autres paires nom=valeur, voici comment je procéderais (en utilisant Perl version 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};

Sur les anciens Perls (au moins 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};

Ceux-ci présentent l’avantage supplémentaire de continuer à fonctionner si vous devez travailler avec davantage de données.

En tant que modification assez simple de la version de Rudd,

/^x=(.+) and y=([^ ]+)(?: and (.*))?/

vous permettra d'utiliser 1 $, 2 $ et 3 $ (le ?:en fait un groupe sans capture) et garantira que la chaîne commence par "x=" plutôt que de permettre à un "not_x=" de correspondre

Si vous avez une meilleure connaissance de ce que seront les valeurs x et y, cela devrait être utilisé pour resserrer davantage l'expression régulière :

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";
    }
}

Sortir:

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: {}

Notez que la partie manquante du dernier test est due au fait que la version actuelle du test y ne nécessite aucun espace, si le test x avait la même restriction, cette chaîne aurait échoué.

Rudd et Cebjyre vous ont mené jusqu'au bout, mais ils ont tous les deux certains problèmes :

Rudd a suggéré :

/x=(.+) et y=([^ ]+)( et (.*))?/

Cebjyre l'a modifié pour :

/^x=(.+) et y=([^ ]+)(? :et (.*))?/

La deuxième version est meilleure car elle ne confondra pas "not_x=foo" avec "x=foo" mais acceptera des éléments tels que "x=foo z=bar y=baz" et définira $1 = "foo z=bar" qui est indésirable.

C'est probablement ce que vous recherchez :

/^x=(\w+) et y=(\w+)(? :et (.*))?/

Cela interdit tout ce qui se trouve entre les options x= et y=, place et autorise les " et..." facultatifs qui seront en 3 $.

Voici essentiellement ce que j'ai fait pour résoudre ce problème :

($x_str, $y_str, $remainder) = split(/ and /, $str, 3);

if ($x_str !~ /x=(.*)/)
{
    # error
}

$x = $1;

if ($y_str !~ /y=(.*)/)
{
    # error
}

$y = $1;

J'ai omis certaines validations supplémentaires et la gestion des erreurs.Cette technique fonctionne, mais elle n’est pas aussi concise ou jolie que je l’aurais souhaité.J'espère que quelqu'un aura une meilleure suggestion à me faire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top