Para quem tem dificuldade em entender lookbehind e lookahead na expressão regular, resolvi tentar explicar, talvez eu consiga ajudá-lo a compreender, e verás que é simples! :)
1. Lookbehind
1.1. (?<=pattern1)pattern2 (zero-width positive lookbehind assertion)
É utilizado para indicar que o pattern2 só irá casar se pattern1 casar antes dele. (positive)
Em outras palavras: Ele verifica se uma expressão (pattern1) casa antes da expressão seguinte. (pattern2)
Para um melhor entendimento, segue um exemplo abaixo:
String: "foobar"
ER: /(?<=foo)bar/
>> Casa!
A ER acima, casou com a string "foobar" porque foi verificado se antes da palavra bar é encontrada a palavra foo, e isso fora constatado.
Observação: Esse construtor trabalha com tamanho fixado. (fixed-width lookbehind) Ou seja, (? não irá funcionar, pois está trabalhando com quantidade não identificada de caracteres, mas (?
Pergunta nº 1: E qual é a diferença em usar /foobar/ diretamente?
Acontece que Lookbehind apenas verifica se a expressão casa antes, ou seja, o que é verificado não fará parte do retorno. (zero-width)
1.2. (?<!pattern1)pattern2 (zero-width negative lookbehind assertion)
É o inverso do que foi visto anteriormente, ou seja, é utilizado para indicar que o pattern2 só irá casar se pattern1 não casar com o que vem antes dele. (negative)
Em outras palavras: Ele verifica se uma expressão (pattern1) não casa antes da expressão seguinte. (pattern2)
Este também trabalha com tamanho fixo. (fixed-width lookbehind)
2. Lookahead
2.1. pattern1(?=pattern2) (zero-width positive lookahead assertion)
É usado para indicar que o pattern1 só irá casar se o pattern2 casar com o que vem após ele. (positive)
Em outras palavras: Ele verifica se uma expressão (pattern2) casa à frente da expressão anterior. (pattern1)
Como os demais, o que é verificado não fará parte do retorno, a menos que o cite na expressão (zero-width)
Como por exemplo:
Usar a expressão /bom (?=(dia))\1/ na string "bom dia", assim o que teve sucesso na verificação sairá no resultado.
2.2. pattern1(?!pattern2) (zero-width negative lookahead assertion)
É o inverso do que foi visto anteriormente, ou seja, pattern1 só casará se após ele não casar o pattern2. (negative)
Em outras palavras: Ele verifica se uma expressão (pattern2) não casa à frente da expessão anterior. (pattern1)
De exemplo vamos supor uma situação: Numa lista de nomes, casar somente nomes que não contenham o sobrenome Silva.
Lista:
- Carlos Silva
- Eduardo Mota
- Felipe Nascimento
- Paulo Ricardo
- Edson Garcia
- Lúcio Silva
ER: /^\w+ (?!Silva)\w+/m
Observação: Foi utilizado o modificador "m" no exemplo, supondo que cada nome esteja em uma linha de um arquivo.
Resultado:
- Eduardo Mota
- Felipe Nascimento
- Paulo Ricardo
- Edson Garcia