Encontrei este excelente tutorial sobre expressões regulares e, embora compreenda intuitivamente o que os quantificadores "gananciosos", "relutantes" e "possessivos" fazem, parece haver um sério buraco no meu entendimento.
Especificamente, no exemplo a seguir:
Enter your regex: .*foo // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.
Enter your regex: .*?foo // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.
Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.
A explicação menciona comer toda a cadeia de entrada, letras sido consumida , matcher recuando , a ocorrência mais à direita de "foo" foi regurgitado , etc.
Infelizmente, apesar das boas metáforas, ainda não entendo o que é comido por quem ... Você conhece outro tutorial que explica (concisa) como os mecanismos de expressões regulares funcionam?
Como alternativa, se alguém puder explicar de maneira um pouco diferente o seguinte parágrafo, isso seria muito apreciado:
O primeiro exemplo usa o quantificador ganancioso. * Para encontrar "qualquer coisa", zero ou mais vezes, seguido pelas letras "f" "o" "o". Como o quantificador é ganancioso, a parte. * Da expressão come primeiro toda a cadeia de entrada. Nesse ponto, a expressão geral não pode ser bem-sucedida, porque as três últimas letras ("f" "o" "o") já foram consumidas ( por quem? ). Portanto, o correspondente recua lentamente ( da direita para a esquerda? ) Uma letra de cada vez até que a ocorrência mais à direita de "foo" seja regurgitada ( o que isso significa? ), Momento em que a correspondência é bem-sucedida e a pesquisa termina.
O segundo exemplo, no entanto, é relutante, então começa consumindo primeiro ( por quem? ) "Nada". Como "foo" não aparece no início da string, é forçado a engolir ( quem engole?) A primeira letra (um "x"), que aciona a primeira correspondência em 0 e 4. Nosso equipamento de teste continua o processo até a sequência de entrada estar esgotada. Encontra outra partida em 4 e 13.
O terceiro exemplo falha ao encontrar uma correspondência porque o quantificador é possessivo. Nesse caso, toda a cadeia de entrada é consumida por. * +, ( Como? ), Não sobrando nada para satisfazer o "foo" no final da expressão. Use um quantificador possessivo para situações em que você deseja aproveitar tudo sem jamais recuar (o que significa recuar? ); superará o quantificador ganancioso equivalente nos casos em que a correspondência não for encontrada imediatamente.
fonte
*
,+
e?
são gananciosos. Mínimas quantificadores gosto*?
,+?
e??
são preguiçosos. Possessivos quantificadores gosto*+
,++
e?+
são pegajoso.Respostas:
Vou tentar.
Um quantificador ganancioso corresponde primeiro ao máximo. Então,
.*
corresponde à seqüência inteira. Em seguida, o correspondente tenta corresponder aof
seguinte, mas não há caracteres restantes. Por isso, "recua", fazendo com que o quantificador ganancioso corresponda a um caractere a menos (deixando o "o" no final da string inigualável). Isso ainda não corresponde aof
da expressão regular, portanto, retrocede mais uma etapa, fazendo com que o quantificador ganancioso corresponda a um caractere a menos novamente (deixando o "oo" no final da sequência inigualável). Isso ainda não coincide com of
no regex, por isso retrocede mais uma etapa (deixando o "foo" no final da string inigualável). Agora, o matcher finalmente combina com of
no regex,o
o
são combinados também. Sucesso!Um quantificador relutante ou "não ganancioso" corresponde primeiro ao mínimo possível. Portanto, a
.*
partida não corresponde a nada, deixando a string inteira incomparável. Em seguida, o correspondente tenta corresponder aof
seguinte, mas a parte incomparável da sequência começa com "x" para que não funcione. Portanto, o correspondente recua, fazendo com que o quantificador não ganancioso corresponda a mais um caractere (agora corresponde ao "x", deixando "fooxxxxxxfoo" inigualável). Em seguida, ele tenta corresponder aof
, que é bem-sucedido, e oo
e o próximoo
no regex também. Sucesso!No seu exemplo, ele inicia o processo novamente com a parte não correspondida restante da sequência "xxxxxxfoo", seguindo o mesmo processo.
Um quantificador possessivo é como o quantificador ganancioso, mas não retrocede. Então, começa com a
.*
correspondência de toda a cadeia, sem deixar nada incomparável. Então não há mais nada para combinar com of
no regex. Como o quantificador possessivo não volta atrás, a correspondência falha lá.fonte
.*+
(note o "+").*+
isso corresponde a tudo. Por exemplo, se você tem um padrão[xyz]*foo
, não há como retroceder os x, y e z correspondentes ao[xyz]*
bit permitirem que ofoo
bit a seguir corresponda, para que você possa acelerar as coisas tornando-o possessivo.É apenas o resultado da minha prática visualizar a cena
fonte
EXPRESSION .*?foo
(), os[f] [o] [o]
retângulos não deveriam estar amarelos no5th pass
?.*?foo
e.*+foo
.Eu nunca ouvi os termos exatos 'regurgitar' ou 'recuar' antes; a frase que os substituirá é "retroceder", mas "regurgitar" parece ser uma frase tão boa quanto qualquer "para o conteúdo que foi tentativamente aceito antes de retroceder o jogou fora".
O importante a ser percebido sobre a maioria dos mecanismos de regex é que eles estão voltando atrás : eles tentativamente aceitam uma correspondência potencial e parcial, enquanto tentam corresponder todo o conteúdo da regex. Se o regex não puder ser completamente correspondido na primeira tentativa, o mecanismo regex retornará em uma de suas correspondências. Ele vai testar a correspondência
*
,+
,?
, alternância, ou{n,m}
repetição de forma diferente, e tente novamente. (E sim, esse processo pode demorar muito.)As últimas três letras,
f
,o
, eo
foram já consumidos pela primeira.*
parte da regra. No entanto, o próximo elemento na regex,,f
não tem mais nada na cadeia de entrada. O mecanismo será forçado a voltar atrás em sua.*
partida inicial e tentar combinar o personagem quase o último. (Pode ser inteligente e voltar aos três últimos, porque possui três termos literais, mas não conheço os detalhes da implementação nesse nível.)Isso significa
foo
que, provisoriamente, foi incluído na correspondência.*
. Como essa tentativa falhou, o mecanismo regex tenta aceitar um caractere a menos.*
. Se houvesse uma correspondência bem-sucedida antes da.*
neste exemplo, o mecanismo provavelmente tentaria encurtar a.*
correspondência (da direita para a esquerda, como você apontou, porque é um qualificador ganancioso) e se não pudesse corresponder todas as entradas, pode ser forçado a reavaliar o que havia correspondido antes do.*
no meu exemplo hipotético.O nada inicial é consumido
.?*
, o que consumirá a menor quantidade possível de qualquer coisa que permita que o restante da regex corresponda.Novamente, ele
.?*
consome o primeiro caractere, após retornar atrás na falha inicial em corresponder a regex inteira com a menor correspondência possível. (Nesse caso, o mecanismo regex está estendendo a correspondência.*?
da esquerda para a direita, porque.*?
é relutante.)A
.*+
consumirá o máximo possível e não retornará para encontrar novas correspondências quando a regex como um todo falhar em encontrar uma correspondência. Porque a forma possessiva não executa retrocesso, você provavelmente não vai ver muitos usos com.*+
, mas sim com classes de personagens ou restrições semelhantes:account: [[:digit:]]*+ phone: [[:digit:]]*+
.Isso pode acelerar drasticamente a correspondência de regex, porque você está dizendo ao mecanismo de regex que ele nunca deve voltar atrás em correspondências em potencial se uma entrada não corresponder. (Se você tivesse que escrever todo o código correspondente à mão, seria semelhante a nunca usar
putc(3)
para 'empurrar' um caractere de entrada. Seria muito semelhante ao código ingênuo que se poderia escrever em uma primeira tentativa. Exceto os mecanismos regex são muito melhor do que um único caractere de retrocesso, eles podem retroceder todo o retorno para zero e tentar novamente. :)Mas, além das possíveis acelerações, isso também permite escrever regexs que correspondem exatamente ao que você precisa. Estou tendo problemas para encontrar um exemplo fácil :), mas escrever um regex usando quantificadores possessivos vs gananciosos pode fornecer correspondências diferentes e um ou outro pode ser mais apropriado.
"Recuar" nesse contexto significa "recuar" - descartando uma correspondência parcial tentativa para tentar outra correspondência parcial, que pode ou não ser bem-sucedida.
fonte
http://swtch.com/~rsc/regexp/regexp1.html
Não sei se essa é a melhor explicação na internet, mas ela é razoavelmente bem escrita e adequadamente detalhada, e continuo voltando a ela. Você pode querer dar uma olhada.
Se você deseja um nível mais alto (explicação menos detalhada), para expressões regulares simples como a que você está visualizando, um mecanismo de expressão regular funciona retornando. Essencialmente, ele escolhe ("come") uma seção da string e tenta combinar a expressão regular com essa seção. Se combinar, ótimo. Caso contrário, o mecanismo altera sua escolha da seção da string e tenta corresponder o regexp a essa seção, e assim por diante, até tentar todas as opções possíveis.
Esse processo é usado recursivamente: na tentativa de combinar uma string com uma determinada expressão regular, o mecanismo dividirá a expressão regular em partes e aplicará o algoritmo a cada parte individualmente.
A diferença entre quantificadores gananciosos, relutantes e possessivos entra quando o mecanismo faz suas escolhas de qual parte da string tentar combinar e como modificar essa opção se não funcionar da primeira vez. As regras são as seguintes:
Um quantificador ganancioso diz ao mecanismo para começar com a sequência inteira (ou pelo menos, tudo isso que ainda não foi correspondido por partes anteriores da expressão regular) e verificar se corresponde à regexp. Se sim, ótimo; o mecanismo pode continuar com o restante da regexp. Caso contrário, ele tenta novamente, mas aparando um caractere (o último) da seção da string a ser verificada. Se isso não funcionar, ele remove outro caractere, etc. Portanto, um quantificador ganancioso verifica possíveis correspondências na ordem do maior para o menor.
Um quantificador relutante diz ao mecanismo para começar com o menor pedaço possível da corda. Se corresponder, o mecanismo pode continuar; caso contrário, ele adiciona um caractere à seção da string que está sendo verificada e tenta isso, e assim sucessivamente até encontrar uma correspondência ou a string inteira ter sido usada. Portanto, um quantificador relutante verifica possíveis correspondências na ordem do menor para o maior.
Um quantificador possessivo é como um quantificador ganancioso na primeira tentativa: ele diz ao mecanismo para começar verificando toda a string. A diferença é que, se não funcionar, o quantificador possessivo informa que a correspondência falhou naquele momento. O mecanismo não altera a seção da string que está sendo examinada e não faz mais tentativas.
É por isso que a correspondência possessiva do quantificador falha no seu exemplo: a
.*+
verificação é feita com relação a toda a cadeia, à qual corresponde, mas o mecanismo continua a procurar caracteres adicionaisfoo
depois disso - mas é claro que não os encontra, porque você já está no final da string. Se fosse um quantificador ganancioso, ele retornaria e tentaria fazer a.*
única correspondência até o penúltimo caractere, depois até o terceiro para o último caractere e, em seguida, até o quarto para o último caractere, o que é bem-sucedido porque só então é há umafoo
esquerda depois de.*
"comer" a parte anterior da corda.fonte
Aqui está minha opinião usando as posições Cell e Index (veja o diagrama aqui para distinguir uma célula de um índice).
Ganancioso - Combine o máximo possível com o quantificador ganancioso e toda a regex. Se não houver correspondência, volte no quantificador ganancioso.
String de entrada: xfooxxxxxxfoo
Regex:. * Foo
O Regex acima tem duas partes:
(i) '. *' E
(ii) 'foo'
Cada uma das etapas abaixo analisará as duas partes. Comentários adicionais para uma correspondência com 'Aprovado' ou 'Reprovado' são explicados entre chaves.
Etapa 1:
(i). * = Xfooxxxxxxfoo - PASS ('. *' É um quantificador ganancioso e usará toda a String de entrada)
(ii) foo = Nenhum caractere a ser correspondido após o índice 13 -
Falha na correspondência.
Etapa 2:
(i). * = Xfooxxxxxxfo - PASS (Retroceder no quantificador ganancioso '. *')
(Ii) foo = o - FAIL A
correspondência falhou.
Etapa 3:
(i). * = Xfooxxxxxxf - PASS (Retrocedendo no quantificador ganancioso '. *')
(Ii) foo = oo - FAIL
correspondência falhou.
Etapa 4:
(i). * = Xfooxxxxxx - PASS (Retroceder no quantificador ganancioso '. *')
(Ii) foo = foo - PASS
Report MATCH
Resultado: 1 correspondência (s)
Encontrei o texto "xfooxxxxxxfoo" começando no índice 0 e terminando no índice 13.
Relutante - corresponda o mínimo possível ao quantificador relutante e corresponda a toda a regex. se não houver correspondência, adicione caracteres ao quantificador relutante.
String de entrada: xfooxxxxxxfoo
Regex:. *? Foo
O regex acima tem duas partes:
(i) '. *?' e
(ii) 'foo'
Etapa 1:.
*? = '' (em branco) - PASS (Corresponde o mínimo possível ao quantificador relutante '. *?'. O índice 0 com '' é uma correspondência.)
foo = xfo - FAIL (célula 0,1,2 - isto é, índice entre 0 e 3) A
correspondência falhou.
Etapa 2:.
*? = x - PASS (Adicione caracteres ao quantificador relutante '. *?'. A célula 0 com 'x' é uma correspondência.)
foo = foo - PASS
Report MATCH
Etapa 3:.
*? = '' (em branco) - PASS (Corresponde o mínimo possível ao quantificador relutante '. *?'. O índice 4 com '' é uma correspondência.)
foo = xxx - FAIL (célula 4,5,6 - ie índice entre 4 e 7) A
correspondência falhou.
Etapa 4:.
*? = x - PASS (Adicione caracteres ao quantificador relutante '. *?'. Célula 4.)
foo = xxx - FAIL (Célula 5,6,7 - ie índice entre 5 e 8)
correspondência falhou.
Etapa 5:.
*? = xx - PASS (Adicione caracteres ao quantificador relutante '. *?'. Célula 4 a 5.)
foo = xxx - FAIL (Célula 6,7,8 - ou seja, índice entre 6 e 9)
correspondência falhou.
Etapa 6:.
*? = xxx - PASS (Adicione caracteres ao quantificador relutante '. *?'. Célula 4 a 6.)
foo = xxx - FAIL (Célula 7,8,9 - ou seja, índice entre 7 e 10)
correspondência falhou.
Etapa 7:.
*? = xxxx - PASS (Adicione caracteres ao quantificador relutante '. *?'. Célula 4 a 7.)
foo = xxf - FAIL (Célula 8,9,10 - ou seja, índice entre 8 e 11)
correspondência falhou.
Etapa 8:.
*? = xxxxx - PASS (Adicione caracteres ao quantificador relutante '. *?'. Célula 4 a 8.)
foo = xfo - FAIL (Célula 9,10,11 - ou seja, índice entre 9 e 12)
correspondência falhou.
Etapa 9:.
*? = xxxxxx - PASS (Adicione caracteres ao quantificador relutante '. *?'. Célula 4 a 9.)
foo = foo - PASS (Célula 10,11,12 - ou seja, índice entre 10 e 13)
FICHA DE JOGO
Etapa 10:.
*? = '' (em branco) - PASS (Corresponde o mínimo possível ao quantificador relutante '. *?'. O índice 13 está em branco.)
foo = Nenhum caractere a ser correspondido - FAIL (Não há nada após o índice 13 a corresponder)
Jogo falhou.
Resultado: 2 correspondências
Encontrei o texto "xfoo" começando no índice 0 e terminando no índice 4.
Encontrei o texto "xxxxxxfoo" começando no índice 4 e terminando no índice 13.
Possessivo - Corresponde o máximo possível ao quantificador possessivo e corresponda a toda a expressão regular. NÃO volte atrás.
String de entrada: xfooxxxxxxfoo
Regex: * + Foo
O regex acima tem duas partes: '. * +' E 'foo'.
Etapa 1:.
* + = Xfooxxxxxxfoo - PASS (Corresponde o máximo possível ao quantificador possessivo '. *')
Foo = Nenhum caractere a ser correspondido - FAIL (Nada a corresponder após o índice 13) A
correspondência falhou.
Nota: O retorno não é permitido.
Resultado: 0 resultado (s)
fonte
Ganancioso: "corresponde à maior seqüência possível de caracteres"
Relutante: "corresponde à menor seqüência possível de caracteres"
Possessivo: Isso é um pouco estranho, pois NÃO (ao contrário de ganancioso e relutante) tenta encontrar uma correspondência para todo o regex.
A propósito: Nenhuma implementação do correspondente do padrão regex jamais usará o retorno. Todos os padrões de vida real são extremamente rápidos - quase independentes da complexidade da expressão regular!
fonte
A quantificação gananciosa envolve a correspondência de padrões usando todos os caracteres não validados restantes de uma sequência durante uma iteração. Caracteres não validados iniciam na sequência ativa . Toda vez que uma partida não ocorre, o personagem no final é colocado em quarentena e a verificação é realizada novamente.
Quando apenas as condições iniciais do padrão regex são satisfeitas pela sequência ativa, é feita uma tentativa de validar as condições restantes contra a quarentena. Se essa validação for bem-sucedida, os caracteres correspondentes na quarentena serão validados e os caracteres não correspondentes residuais permanecerão sem validação e serão usados quando o processo começar novamente na próxima iteração.
O fluxo de caracteres é da sequência ativa para a quarentena. O comportamento resultante é que o máximo possível da sequência original seja incluído em uma correspondência.
A quantificação relutante é basicamente a mesma que a qualificação gananciosa, exceto que o fluxo de caracteres é o oposto - ou seja, eles começam na quarentena e fluem para a sequência ativa . O comportamento resultante é que o mínimo da sequência original seja incluído em uma correspondência possível.
A quantificação possessiva não possui quarentena e inclui tudo em uma sequência ativa fixa .
fonte