Análise de atributos com expressões regulares em Perl
Pergunta
Este é um problema que eu tive recentemente.Eu tenho atributos de cadeias de caracteres do formulário
"x=1 and y=abc and z=c4g and ..."
Alguns atributos têm valores numéricos, alguns têm valores de alfa, alguns têm misturado, alguns têm datas, etc.
Cada cadeia de caracteres é suposto para ter "x=someval and y=anotherval
"no início, mas alguns não.Eu tenho três coisas que eu preciso fazer.
- Validar as cordas para ter certeza de que eles têm
x
ey
. - Na verdade, analisar os valores para
x
ey
. - O restante da seqüência de caracteres.
Dado o exemplo em cima, o resultado seria o seguinte variáveis:
$x = 1;
$y = "abc";
$remainder = "z=c4g and ..."
A minha pergunta é:Existe um (razoavelmente) forma simples de analisar esses e validar com uma única expressão regular?i.e.:
if ($str =~ /someexpression/)
{
$x = $1;
$y = $2;
$remainder = $3;
}
Observe que a seqüência de caracteres pode consistir de apenas x
e y
os atributos.Esta é uma cadeia de caracteres válida.
Eu vou postar a minha solução como uma resposta, mas ela não atender a minha único-regex preferência.
Solução
Eu não sou o melhor em expressões regulares, mas isso parece muito próximo ao que você está procurando:
/x=(.+) and y=([^ ]+)( and (.*))?/
Exceto se você usar o $1, $2 e us $4.Em 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";
}
}
Saída:
x: 1; y: abc; remainder: z=c4g and w=v4l
x: yes; y: no; remainder:
Failed.
Isso, claro, deixa de fora a abundância de verificação de erros, e eu não sei tudo sobre o seu entradas, mas isso parece funcionar.
Outras dicas
Supondo que você também quer fazer algo com outro nome=valor pares isto é como eu faria isso ( usando a versão de 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};
No mais antigos Perls ( pelo 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};
Estes têm a vantagem de continuar a trabalhar, se você precisar trabalhar com mais dados.
Como uma forma bastante simples modificação Rudd versão,
/^x=(.+) and y=([^ ]+)(?: and (.*))?/
irá permitir que você use $1, $2 e $3 (o ?:faz um noncapturing grupo), e irá garantir que a seqüência começa com "x=", ao invés de permitir que um "not_x=" para corresponder
Se você tem mais conhecimento do que os valores de x e y, este deve ser usada para apertar o regex mais:
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";
}
}
Saída:
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: {}
Note que a parte que falta do último teste é devido para a versão atual do y teste necessitando de espaços, se a x teste tinha a mesma restrição de seqüência de caracteres que teria falhado.
Rudd e Cebjyre de ter ficado com você a maior parte do caminho, mas ambos têm alguns problemas:
Rudd sugerido:
/x=(.+) e y=([^ ]+)( e (.*))?/
Cebjyre modificado para:
/^x=(.+) e y=([^ ]+)(?:e (.*))?/
A segunda versão é melhor, porque ele não vai confundir "not_x=foo" com "x=foo", mas irá aceitar as coisas tais como "x=foo z=bar y=baz" e defina us $1 = "foo z= - bar", o que é indesejável.
Este é, provavelmente, o que você está procurando:
/^x=(\w+) e y=(\w+)(?:e (.*))?/
Este não permite qualquer coisa entre x= e y= opções, lugares e permite que e opcional " e..." o que vai ser de us $3
Aqui é basicamente o que eu fiz para resolver este problema:
($x_str, $y_str, $remainder) = split(/ and /, $str, 3);
if ($x_str !~ /x=(.*)/)
{
# error
}
$x = $1;
if ($y_str !~ /y=(.*)/)
{
# error
}
$y = $1;
Eu omiti alguns adicionais de validação e tratamento de erros.Essa técnica funciona, mas não é tão concisa ou bonita como eu teria gostava.Eu estou esperando que alguém vai ter uma melhor sugestão para mim.