Falando sobre Expressões Regulares: Ela é gulosa... Estúpida!
E aí pessoas!
Estou de volta pra continuar de onde paramos no post sobre as chaves e incrementar um pouco mais nossa regex marota pra capturar URLs.
Nossa regex está assim:
Hoje eu quero que ela seja capaz de capturar URLs com domínios sem o “.xx” no final, como aquelas que terminam em “.com”, “.org”, “.co”, etc.
Como de costume, hora de refletir…
Eu já mostrei que o ?
tem a habilidade de tornar um elemento opcional. Mas não é bem isso que eu quero. Pensando bem até é, mas não com o limite de um único caractere. O que eu queria é que todo o bloco fosse opcional. Ou aparecem todos juntos, ou não aparece ninguém.
Sabemos o que queremos. É um excelente começo. A próxima questão é: será que é possível fazer isso em uma regex?
Com certeza. Podemos utilizar os parênteses para agrupar elementos dentro da regex. Os grupos servem para determinar o escopo de alguns operadores. Por exemplo, se colocarmos um quantificador após um grupo, seu efeito vale para todo o grupo, como se o grupo fosse um único elemento na expressão.
Já podemos atualizar nossa expressão. A ideia é agrupar o trecho final \.[a-z]{2}
e torná-lo opcional:
Show de bola!
Muitos sites permitem a omissão do subdomínio “www.”. Pra aceitar estes sites, podemos usar os parênteses em conjunto com o interrogação e tornar o www\.
opcional:
Funciona que é uma beleza!… Mas pera um pouco! Viram aquilo?
Hmmm… Um falso-positivo… Que beleza…
Eu não vou alterar a regex pra parar de capturar este caso porque eu quero evitar a fadiga (Os invejosos dirão que eu não sei resolver o problema). Mas a análise da razão deste match será muito proveitosa para nós.
Bora tentar entender o que aconteceu:
A imagem acima destaca os trechos da regex que deram match com os trechos da URL. Vamos analisar por partes:
https?:\/\/
captura o trecho “http://”;(www\.)?
não captura nada. Por ser um trecho opcional, tudo OK;[a-z-]{3,}\.
captura o trecho “www.”;[a-z]{2,3}
captura o trecho “ab”;(\.[a-z]{2})?
captura o trecho “.co”. Por ser o fim da regex, o match é bem-sucedido.
Excelente pergunta!
Para entender o que está acontecendo de fato, precisamos conhecer uma característica dos operadores quantificadores: eles são gulosos por natureza.
Quando digo que os quantificadores são gulosos, quero dizer que eles tentam capturar o maior trecho que conseguirem, dentro dos limites permitidos. A regra se aplica a todos os quantificadores que vimos nos posts anteriores: ?
, +
, *
e {n,m}
.
Muito boa sacada! Mas muita hora nessa calma, eu já estava chegando lá.
A questão é que uma expressão regular é bem-sucedida apenas se todos os seus componentes forem bem-sucedidos. Se qualquer parte integrante da expressão falhar em capturar o que deve, toda a expressão falha.
Os quantificadores são gulosos, mas não são egoístas a ponto de sacrificar todo mundo apenas pra saciar sua gula. Se capturar o máximo possível significa condenar algum companheiro ao insucesso, o quantificador opta por capturar menos do que lhe é permitido.
Pra clarear as ideias, eu usei toda a minha habilidade artística pra preparar, no Paint, uma imagem que retrata como seria o mundo se os quantificadores fossem egoístas e se colocassem acima do sucesso da missão:
Como podem ver, a partir do momento que o (www\.)?
captura o trecho “www.”, ele condena a expressão regular inteira ao fracasso.
Agora vejam uma imagem retratando o mundo real:
Esta imagem retrata o que está acontecendo de fato com nossa regex, onde o (www\.)?
abre mão de sua gulodice em prol do sucesso de todo o conjunto.
Interessante, não?
Assim, eu encerro o assunto dos quantificadores e me despeço da nossa querida regex pra matching de URLs.
Pois é, hora de desapegar. No próximo post, escreveremos novas expressões e conheceremos uma nova categoria de operadores.
Mas não posso fechar o post antes de atualizar a tabela de operadores:
Itens que batem com um caractere | ||
---|---|---|
Metacaractere | Nome | Função |
. | ponto | captura qualquer caractere |
[ ] | colchetes ou lista | captura qualquer um dos caracteres listados |
\ | barra invertida | torna literal o metacaractere à sua direita |
Modificadores que determinam quantidade: Quantificadores | ||
Metacaractere | Nome | Função |
? | interrogação | torna o elemento à sua esquerda opcional |
* | asterisco | torna o elemento à sua esquerda opcional e permite múltiplas ocorrências |
+ | mais | elemento à sua esquerda deve aparecer uma ou mais vezes |
{n,m} | chaves | elemento à sua esquerda deve aparecer no mínimo n e no máximo m vezes |
Outros (grupo dos que ficaram sem grupo) | ||
Metacaractere | Nome | Função |
( ) | parênteses | delimita escopo para outros operadores |
Valeu pessoas! Até a próxima!
Falou…