Qual é a diferença entre .*? e. * expressões regulares?

142

Estou tentando dividir uma string em duas partes usando regex. A sequência é formatada da seguinte maneira:

text to extract<number>

Eu tenho usado (.*?)<e <(.*?)>que funciona bem, mas depois de ler um pouco sobre regex, comecei a me perguntar por que preciso ?das expressões. Eu só fiz isso depois de encontrá-los neste site, então não sei exatamente qual é a diferença.

Doug
fonte
1
veja também stackoverflow.com/questions/2301285/…
Cemafor:

Respostas:

172

É a diferença entre quantificadores gananciosos e não gananciosos.

Considere a entrada 101000000000100.

Usar 1.*1, *é ganancioso - ele corresponderá até o fim e depois voltará até que possa corresponder 1, deixando você com 1010000000001.
.*?não é ganancioso. *não corresponderá a nada, mas tentará corresponder caracteres extras até corresponder 1, eventualmente correspondendo 101.

Todos os quantificadores têm um modo não-ganancioso: .*?, .+?, .{2,6}?, e mesmo .??.

No seu caso, um padrão semelhante poderia ser <([^>]*)>- corresponder a qualquer coisa, exceto um sinal de maior que (estritamente falando, ele corresponde a zero ou mais caracteres que não sejam >intermediários <e >).

Consulte Folha de dicas do quantificador .

Kobi
fonte
Ah, ótimo, eu gosto desse último de qualquer coisa, menos do sinal>!
Doug
1
Você pode explicar ou mostrar um exemplo de como o ganancioso ?difere do não-ganancioso ???
AdrianHHH 25/11
4
Certo. Para a sequência "abc", o regex /\w\w?\w/corresponderia à sequência completa "abc"- porque ?é ganancioso. /\w\w??\w/é preguiçoso - ele só corresponde "ab". Ele só retornará e corresponderá "abc"se falhar mais tarde.
Kobi
184

Em ganancioso vs não ganancioso

A repetição na regex, por padrão, é gananciosa : eles tentam corresponder o máximo de repetições possível e, quando isso não funciona e precisam voltar, tentam corresponder a menos um representante de cada vez, até que uma correspondência de todo o padrão seja encontrado. Como resultado, quando uma partida finalmente acontece, uma repetição gananciosa corresponderá ao maior número possível de representantes.

O ?quantificador de repetição muda esse comportamento para não ganancioso , também chamado de relutante ( em Java, por exemplo ) (e às vezes "preguiçoso"). Em contraste, esta repetição vai primeiro tentar igualar como alguns representantes quanto possível, e quando isso não funciona e eles têm que voltar atrás, eles começam a correspondência mais uma rept um tempo. Como resultado, quando uma partida finalmente acontece, uma repetição relutante corresponderia ao menor número de repetições possível.

Referências


Exemplo 1: de A a Z

Vamos comparar esses dois padrões: A.*Ze A.*?Z.

Dada a seguinte entrada:

eeeAiiZuuuuAoooZeeee

Os padrões produzem as seguintes correspondências:

Vamos primeiro nos concentrar no que A.*Zfaz. Quando combinou com o primeiro A, o .*, sendo ganancioso, primeiro tenta combinar o maior número .possível.

eeeAiiZuuuuAoooZeeee
   \_______________/
    A.* matched, Z can't match

Como Znão corresponde, o mecanismo retorna e .*deve corresponder a menos um .:

eeeAiiZuuuuAoooZeeee
   \______________/
    A.* matched, Z still can't match

Isso acontece mais algumas vezes, até que finalmente chegamos a isso:

eeeAiiZuuuuAoooZeeee
   \__________/
    A.* matched, Z can now match

Agora Zpode corresponder, portanto, o padrão geral corresponde:

eeeAiiZuuuuAoooZeeee
   \___________/
    A.*Z matched

Por outro lado, a repetição relutante nos A.*?Zprimeiros jogos corresponde ao .mínimo possível e, em seguida, leva o .máximo que for necessário. Isso explica por que ele encontra duas correspondências na entrada.

Aqui está uma representação visual do que os dois padrões corresponderam:

eeeAiiZuuuuAoooZeeee
   \__/r   \___/r      r = reluctant
    \____g____/        g = greedy

Exemplo: uma alternativa

Em muitas aplicações, as duas correspondências na entrada acima são o que se deseja, portanto, um relutante .*?é usado no lugar do ganancioso .*para evitar a superação. Para esse padrão específico, no entanto, existe uma alternativa melhor, usando a classe de caracteres negada.

O padrão A[^Z]*Ztambém encontra as mesmas duas correspondências que o A.*?Zpadrão para a entrada acima ( como visto em ideone.com ). [^Z]é o que é chamado de classe de caracteres negada : corresponde a qualquer coisa, menos Z.

A principal diferença entre os dois padrões está no desempenho: sendo mais rigorosa, a classe de caracteres negados pode corresponder apenas a um caminho para uma determinada entrada. Não importa se você usa modificador ganancioso ou relutante para esse padrão. De fato, em alguns sabores, você pode se sair ainda melhor e usar o que é chamado de quantificador possessivo, que não retrocede.

Referências


Exemplo 2: de A a ZZ

Este exemplo deve ser ilustrativo: mostra como os padrões de classe de caracteres gananciosos, relutantes e negados são diferentes de acordo com a mesma entrada.

eeAiiZooAuuZZeeeZZfff

Estas são as correspondências para a entrada acima:

Aqui está uma representação visual do que eles corresponderam:

         ___n
        /   \              n = negated character class
eeAiiZooAuuZZeeeZZfff      r = reluctant
  \_________/r   /         g = greedy
   \____________/g

tópicos relacionados

Esses são links para perguntas e respostas no stackoverflow que cobrem alguns tópicos que podem ser do seu interesse.

Uma repetição gananciosa pode superar outra

poligenelubricants
fonte
1
Eu quis dizer rubular.com, não ideone.com. Para outros: não revise este post para mim; eu mesmo o farei na próxima revisão, junto com outros exemplos. Sinta-se livre para dar feedback, sugestão, etc. nos comentários, para que eu possa incorporá-los também.
polygenelubricants
2
Veja também: stackoverflow.com/questions/3145023/…
polygenelubricants
4
Essa resposta foi adicionado ao Stack Overflow Expressão FAQ regular , em "Quantifiers> Mais informações sobre as diferenças ..."
aliteralmind
Esta resposta realmente merece ser a resposta escolhida! Muito obrigado pela sua explicação detalhada.
precisa saber é o seguinte
Eu adicionei a tag não gananciosa . Por que, porque a pergunta era necessária, mas também porque canalizaria mais usuários para essa ótima resposta. Em outras palavras, se você der uma ótima resposta e a resposta usar uma tag que não está na pergunta, adicione-a porque o OP não sabia que a tag era reveladora.
Guy Coder
20

Digamos que você tenha:

<a></a>

<(.*)>corresponderia a></aonde como <(.*?)>corresponderia a. O último pára após a primeira partida de >. Ele verifica se há uma ou 0 correspondências .*seguidas pela próxima expressão.

A primeira expressão <(.*)>não para quando corresponde à primeira >. Continuará até a última partida de >.

Simon
fonte
isso é mais fácil de entender do que a explicação acima.
Prometheus