Domanda

Ecco un problema che ho incontrato di recente.Ho gli attributi stringhe della forma

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

Alcuni attributi sono valori numerici, alcuni hanno valori di alfa, alcuni sono contrastanti, alcuni hanno date, etc.

Ogni stringa è dovrebbe per avere "x=someval and y=anotherval"all'inizio, ma alcuni non lo fanno.Ho tre cose che devo fare.

  1. Convalidare le corde per essere certi che essi hanno x e y.
  2. In realtà analizzare i valori per x e y.
  3. Ottenere il resto della stringa.

Dato l'esempio in alto, questo comporterebbe le seguenti variabili:

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

La mia domanda è:C'è un (ragionevolmente) modo semplice per analizzare questi e confermare con un semplice espressione regolare?cioè:

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

Si noti che la stringa può consistere in solo x e y attributi.Questa è una stringa valida.

Vi posto la mia soluzione come risposta, ma non soddisfa il mio singolo-regex preferenza.

È stato utile?

Soluzione

Io non sono la migliore delle espressioni regolari, ma questo sembra abbastanza vicino a quello che si sta cercando:

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

Tranne che in $1, $2, $4.In 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";
    }
}

Output:

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

Questo, naturalmente, lascia fuori un sacco di controllo di errore, e non so tutto del tuo input, ma questo sembra funzionare.

Altri suggerimenti

Supponendo che anche voi volete fare qualcosa con le altre coppie nome=valore, questo è come vorrei farlo ( usando la versione di Perl 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};

Sui vecchi Perls ( almeno 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};

Questi hanno il vantaggio di continuare a lavorare, se hai bisogno di lavorare con più dati.

Come una semplice modifica al Timone della versione,

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

vi permetterà di utilizzare $1, $2 e $3 (l' ?:rende un gruppo di non cattura), e farà in modo che la stringa inizia con "x=" piuttosto che un "not_x=" a partita

Se si dispone di una migliore conoscenza di ciò che i valori x e y sarà, questo dovrebbe essere utilizzata per stringere la regex ulteriormente:

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

Output:

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

Nota che la parte mancante dell'ultimo test è dovuto alla versione corrente di y test che richiedono spazi, se x test aveva la stessa limitazione che stringa sarebbe fallito.

Rudd e Cebjyre hanno ottenuto più del modo in ci ma hanno alcuni problemi:

Rudd ha suggerito:

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

Cebjyre modificato per:

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

La seconda versione è migliore, perché non confondere "not_x=pippo" con "x=pippo", ma accettare le cose come "x=pippo z=bar y=baz" e impostare $1 = "pippo z=bar" che è indesiderabile.

Questo è probabilmente ciò che si sta cercando:

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

Questo non consente nulla tra x= e y= opzioni, luoghi e permette di optional e...", che sarà in $3

Ecco, fondamentalmente, ciò che ho fatto per risolvere questo problema:

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

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

$x = $1;

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

$y = $1;

Ho omesso qualche ulteriore convalida e la gestione degli errori.Questa tecnica funziona, ma non è più conciso, più o meno, come mi sarebbe piaciuto.Sto sperando che qualcuno ha un suggerimento migliore per me.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top