Parsarea atributelor cu regex în Perl

Iată o problemă la care am fugit recent. Am atributele șiruri ale formularului

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

Unele atribute au valori numerice, unele au valori alfa, altele au amestec, unele au date etc.

Fiecare șir este presupus pentru a avea " x = someval și y = anotherval " la început, dar unele nu. Am trei lucruri pe care trebuie să le fac.

  1. Validează șirurile pentru a fi siguri că au x și y .
  2. De fapt, parsează valorile x și y .
  3. Obțineți restul șirului.

Având în vedere exemplul din partea de sus, acest lucru ar avea ca rezultat următoarele variabile:

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

Întrebarea mea este: Există o modalitate (rezonabil) simplă de a analiza aceste și validare cu o singură expresie regulată? .: adică

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

Rețineți că șirul poate conține atributele numai x și y . Acesta este un șir valid.

Voi posta soluția mea ca răspuns, dar nu corespunde preferințelor mele cu un singur regex.

0
fr hi bn

5 răspunsuri

Nu sunt cel mai bun la expresii regulate, dar acest lucru pare destul de aproape de ceea ce căutați:

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

Cu excepția faptului că utilizați $ 1, $ 2 și $ 4. In folosinta:

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

ieşire:

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

Acest lucru, desigur, lasa o multime de verificari a erorilor si nu stiu totul despre intrarile dvs., dar acest lucru pare sa functioneze.

0
adăugat

Iată în esență ceea ce am făcut pentru a rezolva acest lucru:

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

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

$x = $1;

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

$y = $1;

Am omis unele validaturi suplimentare și tratarea erorilor. Această tehnică funcționează, dar nu este la fel de concisă sau drăguță cum mi-ar fi plăcut. Sper că cineva va avea o sugestie mai bună pentru mine.

0
adăugat
Acest lucru mi se pare mai simplu și mai sustenabil decât oricare dintre soluțiile "un regexp pentru a le guverna pe toate". Aș putea adăuga doar un ^ la începutul acolo, pentru a se potrivi x = și y = pentru a evita cazul not_x = ... sau similar. De ce vrei un singur regexp?
adăugat autor mirod, sursa

Rudd și Cebjyre ți-au adus cea mai mare parte acolo, dar ambii au anumite probleme:

Rudd a sugerat:

/x = (+) și y = ([^] +) (și (. *))

Cebjyre a modificat-o pentru:

/^ x = (+) și y = ([^] +) (a: și (*

A doua versiune este mai bună pentru că nu va confunda "not_x = foo" cu "x = foo", dar va accepta lucruri precum "x = foo z = bar y = baz" și seta $ 1 = "foo z = bar" indezirabil.

Aceasta este probabil ceea ce căutați:

/^ x = (\ w +) și y = (\ w +) (a și <

Acest lucru interzice orice dintre opțiunile x = și y =, locurile și permite și opțional "și ...", care va fi în $ 3

0
adăugat

Presupunând că doriți să faceți ceva și cu celelalte nume = perechi de valori, așa aș face eu (folosind Perl versiunea 5.10):

use 5.10.0;
use strict;
use warnings;

my %hash;
while(
    $string =~ m{
       (?: ^ | \G )    # start of string or previous match
       \s*

       (?   \w+ ) # word characters
       =
       (? \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};

Pe Perls mai în vârstă (cel puțin 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};

Acestea au avantajul suplimentar de a continua să lucrați dacă aveți nevoie să lucrați cu mai multe date.

0
adăugat
\ G se potrivește deja cu începutul șirului, astfel încât să puteți înlocui (?: ^ | \ G) cu \ G . Dar o modalitate mai bună este de a plasa \ G în factor la început și de a muta și la început: \ G (?: ^ | \ S + \ s +) (\ w +) = (\ S +)
adăugat autor Casimir et Hippolyte, sursa
+1 exemplu frumos de tampoane de captură numit!
adăugat autor Ben Deutsch, sursa

Ca o modificare destul de simplă a versiunii lui Rudd,

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

vă va permite să utilizați $ 1, $ 2 și $ 3 (grupul ?: îl face un grup care nu captează) și se va asigura că șirul începe cu "x =", în loc să permită o potrivire "not_x ="

Dacă aveți o mai bună cunoaștere a valorii x și y, aceasta ar trebui utilizată pentru a strânge mai mult regexul:

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

ieşire:

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

Rețineți că partea lipsă a ultimului test se datorează versiunii curente a testului y care nu necesită spații, dacă testul x a avut aceeași restricție ca șirul de eșantioane ar fi eșuat.

0
adăugat