Regex sem medo: de uma vez por todas (parte 2)

Prosseguindo com o post anterior, vamos falar sobre o operador “ou”, grupos e substituição. A propósito: eu tinha pego um texto do tipo Lorem Ipsum para utilizar de alvo nos exemplos anteriores, mas confesso que ficou bem bosta. Portanto, a partir de agora, vou utilizar um texto que se adapte melhor ao contexto do problema.

Operador “|”: um ou outro

Imagine uma lista de URLs entre as quais você queira encontrar as que utilizam http e https. Você poderia simplesmente utilizar a seguinte regex:

/https?:\/\/.+/

Screenshot from 2017-08-04 21-24-54

Que diz ao interpretador:

um “http”, seguido ou não de um “s”, seguido de uma “/”, seguido de outra “/”, de um ou mais qualquer coisa.

Neste caso, as barras estão escapadas (cada uma com uma contra barra) para evitar que que elas sejam interpretadas como o final da regex, já que elas são representadas sempre entre duas barras.

Agora imagine que você precisa casar também as URLs que utilizam o protocolo FTP. Utilizando os coringas e as listas de opções que foram apresentadas até agora, isso não seria possível (pelo menos não de um jeito fácil). É para isso que serve o operador “|” (ou); separar duas ou mais opções de busca. A regex ficaria assim:

(http|https|ftp):\/\/.+

Screenshot from 2017-08-04 21-51-22

Que diria:

um “http”, ou um “https”, ou um “ftp” seguido de uma “/”, seguido de outra “/”, de um ou mais qualquer coisa.

Repare que além do operador “|” foram inseridos os caracteres de agrupamento “(” e “)” para delimitar o grupo de opções.

Sem os parênteses:

/http|https|ftp:\/\/.+/

O resultado seria:

Screenshot from 2017-08-04 22-21-19
Porque o interpretador entenderia que você procura cada um dos blocos isoladamente, como se fosse 3 regexes:

/http//https/ ou /ftp:\/\/.+/

Outra alternativa seria você definir as 3 regexes separadas por “|”:

/http:\/\/.+|https:\/\/.+|ftp:\/\/.+/

Screenshot from 2017-08-04 22-35-35

(…) que, na minha opinião, não fica nem bonito nem muito inteligente. Mas funciona! 

Outra vantagem do agrupamento (ou grupo) é que ele cria referências para cada match chamadas de retrovisores (ou back reference) que podem ser utilizadas na em casos de substituição. Funciona assim: cada item de grupo casado é armazenado em uma variável para ser utilizada em uma operação de substituição. Por exemplo, o primeiro match do grupo fica em \1, o segundo em \2 e o eNésimo no \N. Exemplos explicam mais: vamos mascarar alguns CEPs utilizando a ferramenta de substituição do regex101.com bem na parte inferior da tela:

Screenshot from 2017-08-04 22-56-03

É só clicar sobre o SUBSTITUTION que ele a tela expande com o preview das substituições.

Substituindo

O CEP é algo mais ou menos assim XX.YYY-ZZZ, em que Z, Y e Z são inteiros entre 0 e 9. Portanto a regex fica:

/([0-9]{2})([0-9]{3})([0-9]{3})/

Ou seja:

Dois caracteres entre 0 e 9 (primeiro grupo: \1), seguidos de mais três caracteres entre 0 e 9 (segundo grupo: \2), seguido por mais três caracteres entre 0 e 9 (terceiro grupo: \3).

Vale acrescentar aqui cada um dos “0-9” da regex anterior poderia ser substituído por um “\d” – de dígito – que funciona exatamente da mesma forma. Falaremos sobre esses meta caracteres em outra ocasião, mas tenta lá.

E vou separar cada um dos grupos com o caractere adequado da máscara:  ou um “.” ou um “-“:

\1.\2-\3

Que é interpretado:

Primeiro match seguido por um ponto, seguido pelo segundo match seguido por um hífen, seguido pelo terceiro match. 

E o resultado:

Screenshot from 2017-08-04 23-09-42

Encerrando…

Ficou um pouco mais longo do que eu tinha imaginado inicialmente, portanto, vou encerrar por aqui. Continuamos na parte 3. Sugestões e críticas são mais bem-vindas que boletos e dívidas.

Obrigado por ler (ou “por ter lido”?) e até o próximo.

Regex sem medo: de uma vez por todas (parte 1)

Eu aprendi expressões regulares com o livro do Aurélio Jargas, que além de me ensinar, me motivou aprender a explicar as coisas de forma objetiva, clara e rápida. Foi um dos melhores livros de TI que eu já li e recomendo veementemente que você tenha uma cópia.

Expressões regulares, ou regex, é uma ferramenta de manipulação de texto que vai lhe permitir simplificar suas tarefas do dia-a-dia. Mesmo que você não seja um programador, certamente já deve ter encarado situações em que tinha que localizar ou até mesmo substituir um texto em um documento.

Para desenvolver e testar as expressões, eu costumo utilizar algumas ferramentas online, entre elas o Regexpal e o Regex101. As interfaces são bem simples: basicamente um campo para você inserir a regex, outro para inserir um texto de teste e um bloco para inserir a expressão de substituição.

Geralmente as regex são representadas entre duas barras (/regex/) seguida por alguns extras de configuração (/regex/ig), os quais vamos discutir mais adiante.

Pau na mula!

O exemplo mais básico é você procurar por um texto literal:

/et/

Vai buscar pelo texto “et”.

Screenshot from 2017-07-21 22:08:46

Até aí nada demais, qualquer editor de textos com uma ferramenta de busca faz isso. Você também deve ter percebido que qualquer ocorrência de “et” foi casada, seja ela isolada ou compondo outra palavra, como no caso de “amet” e “consectetur”. Se você você quisesse optar por encontrar somente a palavra “et” isolada, você poderia usar o delimitador de bordas: “\b”. A regex ficaria assim:

/\bet\b/

Screenshot from 2017-07-29 00-32-21

Os “\b” envolvendo a palavra “et” diz ao interpretador que a ocorrência deve ser uma palavra isolada.

Coringa ponto

O “.” (ponto) é o coringa mais básico. Ele quer dizer “qualquer coisa”. Por exemplo, se você quisesse buscar por todos os “e’s” seguidos por qualquer coisa:

/e./

Screenshot from 2017-07-23 17-24-39

O ponto diz ao interpretador:

um “e” seguido por qualquer coisa.

Repare que o “qualquer coisa” é qualquer coisa mesmo, inclusive um espaço em branco.

Lista de opções

As listas de opções entram em ação quando o ponto é demais, ou seja, você precisa delimitar as opções que deseja casar. Veja o exemplo:

/e[tx]/

Screenshot from 2017-07-21 22:14:47

Os colchetes ([ e ]) delimitam as listas de opções. O interpretador vai ler assim:

um “e” seguido por um “t” ou “x”.

Veja como fica se adicionarmos, por exemplo, um “a” na lista:

/e[txa]/

Screenshot from 2017-07-21 22:17:39

Neste caso, o interpretador passa a ler:

um “e” seguido de um “t”, um “x” ou um “a”.

As listas também permitem definir intervalos como “de a à z” ou “de 0 à 9”:

/e[a-z0-9]/

Screenshot from 2017-07-23 16-42-30

Como não temos nenhum caractere numérico no texto, o resultado destaca somente as letras “e” seguidas por qualquer caractere que esteja entre “a” e “z”, como especificado pela lista.

Lista negada

Ao invés de especificar o que você quer casar, você pode especificar o que você não quer.

/e[^\s\n,.-]/

usando um “^” no ínicio, a lista passa a funcionar com uma lista de opções que não serão aceitas:

Screenshot from 2017-07-23 22-43-46

qualquer “e” que não seja seguido por um espaço em branco (“\s”), quebra de linha (“\n”), uma vírgula (“,”), um ponto “.” ou um hífen (“-“).

Tanto faz usar um ” ” (espaço em branco) ou um “\s”. O critério é seu. Gosto do \s porque é mais fácil de visualizar. Já quanto ao hífen, o final da lista é o único lugar onde o ele pode aparecer sem estar escapado, ou seja, sem estar precedido por uma contra-barra. Em qualquer outra posição, ele seria considerado um operador de intervalo entre os caracteres que o precedessem e sucedessem. Já o ponto dentro da lista é um ponto mesmo; deixa de ter o efeito de um coringa.

Quantificadores

Os quantificadores servem para delimitar a quantidade de matches que você quer. São 4 tipos de quantificadores:

  • ? (interrogação): que quer dizer “tem ou não”  ou “zero ou um”;
  • * (asterisco): que quer dizer “zero ou mais“;
  • + (mais): que quer dizer “um ou mais“;
  • {x} (chaves): que quer dizer “exatamente x“, sendo x um número inteiro positivo ou um intervalo válido ex: {1,5} (de um a cinco), {0,9} (de zero a nove), etc.

Vamos modificar uma das regex anteriores para utilizar o quantificador “?”:

Quantificador “?”: tem ou não

/e[txa]?/

Screenshot from 2017-07-23 16-59-46

A expressão /e[txa]?/ diz para o interpretador:

um “e” seguido por zero ou um “t”, zero ou um “x”, ou zero ou um “a”, inclusive nenhum.

O “inclusive nenhum” é o que faz com que o e, do Lorem, seja marcado como match, já que ele não é sucedido por nenhuma das opções [txa]. Vale lembrar também que quantificadores são fixados imediatamente após ao valor que eles vão quantificar. Portanto, a interrogação na regex anterior vale somente para lista “[txa]” e não para o “e”.

Quantificador “*”: zero ou mais

O “*” funciona de forma similar ao “?”, porém, ao invés de zero ou um ele quantifica zero ou mais:

/e[txa]*/

Screenshot from 2017-07-23 16-50-47

Essa regex diz ao interpretador:

um “e” seguido de um ou mais “t”, um “x” ou um “a”, inclusive um “e” seguido de nada. 

Se trocarmos o “*” pelo “+”, o interpretador vai deixar de casar “e’s” que não estejam sucedidos por um “t”, “x” ou “a”.

Quantificador “+”: um ou mais

/e[txa]+/

Screenshot from 2017-07-23 16-55-23

Justamente por dizer “um ou mais” o quantificador “+” deixa de casar os casos de “e’s” seguidos por nenhum “t”, “x” ou “a”, como acontecia com o “*”.

Quantificador {x}

Quando você precisa quantificar número específico de matches, você usa as chaves. Por exemplo, vamos criar uma regex para casar todos os “e’s” seguidos por duas vogais:

/e[a-z]{2}/

Screenshot from 2017-07-23 22-25-45

O interpretador lerá:

um “e” seguido por um “a”, ou “e”, ou “i”, ou “o”, ou “u” exatamente duas vezes.

E como foi dito, você pode especificar intervalos:

/e[a-z]{3,5}/

Screenshot from 2017-07-23 22-30-31

qualquer “e” seguido por 3, 4, ou 5 caractere entre “a” e “z”.

Vale ressaltar que “{3,}” também é um intervalo válido. Neste caso “{3,}” quer dizer “três ou mais”, mas vou deixar esse teste para você fazer.

Encerrando…

Tendo sempre em mente escrever posts de 5 minutos, encerramos por aqui a primeira parte do tutorial das regex. Nos posts seguintes pretendo apresentar os blocos, os operadores de gulodice e algumas regex que utilizo no meu dia-a-dia.

Por hora é só.
Até o próximo.