Backtracking control verbs com exemplos

Para quem não sabe, Perl 5.10 introduziu um novo recurso que permite controlar o backtracking realizado pela engine das expressões regulares. Isso dá um poder a mais quando se escreve uma regex. Demonstrarei abaixo alguns simples casos onde poderíamos lançar mão deste recurso através da lib. PCRE.

Vale lembrar que anteriormente já havia postado sobre um dos backtracking control verbs, o (*COMMIT), para quem ainda não viu: http://felipe.ath.cx/special-backtracking-control-verbs

Observação: Os testes que serão vistos a seguir foram feitos com o pcretest (versão 8.12), incluído no tarball da PCRE.

(*ACCEPT)

Este comando faz com que o matching termine com sucesso logo que encontrado. Se estiver dentro de um grupo, a informação é capturada até ele.

  re> /foo((?:a|bar(*ACCEPT))zzz)/
data> foobar
 0: foobar
 1: bar
data> fooa
No match
data> fooazzz
 0: fooazzz
 1: azzz

Como podemos ver, quando tentamos casar "foobar", ele não requer que termine com zzz, porque o matching é logo terminado por encontrar o (*ACCEPT) na alternativa do 'bar'.
Mas se a alternativa usada for o 'a', como não tem o (*ACCEPT), ele vai seguir o fluxo normalmente, precisando então que tenha o 'zzz' em seguida.

Entendido!? Próximo! :)

(*FAIL) ou (*F)

Este comando faz com que o matching falhe, causando o backtring.

  re> /foo(?:bar(*F))?/
data> foo
 0: foo
data> foobar
 0: foo

Como podem ver, a engine desiste de casar 'foobar' quando é possível, fazendo o backtracking, e casando apenas 'foo'.

(*MARK:NAME) ou (*:NAME)

Este comando é interessante, ele serve para registrar um nome indicando que parte da regex foi examinada por último. É como colocar um printf marcando que parte do código foi executado.

  re> /foo(?:bar(*:A)|baz(*:B))/K
data> foobar
 0: foobar
MK: A
data> foobaz
 0: foobaz
MK: B

Se você usa a lib PCRE, esse nome pode ser obtido na estrutura pcre_extra.

(*COMMIT)

Como dito no início do post, já havia comentado e exemplificado o uso deste comando. Ele simplesmente indica a engine que não poderá haver backtracking para parte do matching anterior ao ponto que ele é encontrado.

  re> /foo(?:b(*COMMIT)c|bd)/
data> foobd
No match
data> foobc
 0: foobc

Como podem ver, pela engine ter tentado casar primeiramente 'foobc', ele não permite a engine testar a outra alternativa 'bd', visto que ele teve que passar por um (*COMMIT) e então ver que a expressão não casa com 'bd'.

(*PRUNE) ou (*PRUNE:NAME)

Com este comando fazemos com que o backtracking volte para o início do matching caso haja uma falha após ele ser encontrado.

  re> /foo(?:b(*PRUNE)c|bd)|bd/
data> foobd
 0: bd
data> foobc
 0: foobc

(*SKIP) ou (*SKIP:NAME)

Quando este é usado sem um nome, ele é parecido com (*PRUNE), exceto quando a expressão não é ancorada (isto é, não começa com ^). Na falha do matching a engine não pula para o próximo caractere, mas para a posição onde o (*SKIP) aparece em relação a string a ser casada.

  re> /foo(?:b(*SKIP)c|bd)|bd/
data> foobc
 0: foobc
data> foobd
No match

(*THEN) ou (*THEN:NAME)

Este comando faz com que a engine tente a próxima alternativa do grupo atual caso ocorra uma falha após ele ser encontrado. Sem fazer backtracking dentro da alternativa atual.

  re> /(?:b.+r|car)d/
data> bardcard
 0: bardcard
  re> /(?:b.+(*THEN)r|car)d/
data> bardcard
 0: card

Há vários detalhes que não foram cobertos por este post, os quais podem ser lidos na página da PCRE: http://www.pcre.org/pcre.txt

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options