Como posso consertar meu regex para não coincidir muito com um quantificador ganancioso? [duplicado]

StackOverflow https://stackoverflow.com/questions/255815

Pergunta

Esta questão já tem uma resposta aqui:

Eu tenho a seguinte linha:

"14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)"

eu analisar isso usando uma expressão regular simples:

if($line =~ /(\d+:\d+)\ssay;(.*);(.*);(.*);(.*)/) {
    my($ts, $hash, $pid, $handle, $quote) = ($1, $2, $3, $4, $5);
}

Mas o; nas messes finais as coisas e eu não sei porquê. Caso não a alça operador gananciosos "tudo"?

Foi útil?

Solução

O operador gananciosos tenta agarrar tanta coisa que ele pode e ainda coincidir com a string. O que está acontecendo é o primeiro (depois de "dizer") agarra "0ed673079715c343281355c2a1fde843; 2"., A segunda leva "Laka", o terceiro achados "Olá" e quarta partidas o parêntese

O que você precisa fazer é fazer tudo mas o último não-ganancioso, então eles pegam o mínimo possível e ainda coincidir com a string:

(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)

Outras dicas

(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)

deve funcionar melhor

Apesar de um regex pode facilmente fazer isso, eu não tenho certeza que é a abordagem mais direta. É provavelmente o mais curto, mas que na verdade não torná-lo mais sustentável.

Em vez disso, eu sugiro algo como isto:

$x="14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)";

if (($ts,$rest) = $x =~ /(\d+:\d+)\s+(.*)/)
{
    my($command,$hash,$pid,$handle,$quote) = split /;/, $rest, 5;
    print join ",", map { "[$_]" } $ts,$command,$hash,$pid,$handle,$quote
}

Isso resulta em:

[14:48],[say],[0ed673079715c343281355c2a1fde843],[2],[laka],[hello ;)]

Eu acho que isso é apenas um pouco mais legível. Não só isso, eu acho que é também mais fácil de depurar e manter, porque isso está mais perto de como você faria se um ser humano tentasse a mesma coisa com caneta e papel. Quebrar a corda em pedaços, que você pode analisar mais fácil - têm o computador fazer exatamente o que você faria. Quando chega a hora de fazer modificações, eu acho que este se sairá melhor. YMMV.

Tente fazer o primeiro 3 (.*) ungreedy (.*?)

Se os valores na sua lista delimitada por ponto e vírgula não pode incluir-se qualquer ponto e vírgula, você vai ter a expressão regular mais eficiente e direta simplesmente soletrar isso. Se certos valores só pode ser, por exemplo, uma sequência de caracteres hexadecimais, soletrar isso. Soluções usando um ponto preguiçoso ou ganancioso vai sempre levar a um monte de retrocesso inútil quando a regex não coincide com a seqüência de assunto.

(\d+:\d+)\ssay;([a-f0-9]+);(\d+);(\w+);([^;\r\n]+)

Você poderia fazer * não-ganancioso anexando um ponto de interrogação:

$line =~ /(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)/

ou você pode combinar tudo, exceto um ponto e vírgula em cada parte, exceto a última:

$line =~ /(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)/
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top