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.

  1. Validar as cordas para ter certeza de que eles têm x e y.
  2. Na verdade, analisar os valores para x e y.
  3. 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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top