Como posso consertar meu regex para não coincidir muito com um quantificador ganancioso? [duplicado]
-
05-07-2019 - |
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"?
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;([^;]*);([^;]*);([^;]*);(.*)/