L'analisi di attributi con espressioni regolari in Perl
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.
- Convalidare le corde per essere certi che essi hanno
x
ey
. - In realtà analizzare i valori per
x
ey
. - 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.
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.