Expressões Regulares

Chega de Ctrl+c, Ctrl+v em expressões enigmáticas no Stackoverflow para validar aquele campo de data ou utilizar bibliotecas com centenas de outras funções só para validar um campo de email. Vamos entender como trabalhar com expressões regulares!

Vamos lá!

O que é uma expressão regular?

Expressão regular (também conhecida como regex ou regexp) nada mais é do que uma sequência de caracteres que nos permite identificar outra sequência de caracteres em um texto ou uma frase por exemplo, através da correspondência de padrões. São utilizadas em editores de texto, formulários de sites (onde você é obrigado a digitar apenas números ou apenas caracteres), em linguagens de programação, etc.

Expressões Regulares em JavaScript

Em JavaScript existem duas maneiras de se criar uma expressão regular:

Usando expressão literal

let er = /abc/

Ou chamando o construtor do objeto RegExp

let er =  new RegExp("abc");

Ambas representam o mesmo padrão com algumas diferenças. No primeiro caso delimitamos a expressão pelas barras e a compilação ocorre durante o carregamento do script, já o segundo caso segue as mesmas regras do primeiro, porém passamos uma string como parâmetro e a compilação é realizada durante o tempo de execução.

É importante notar que alguns caracteres, como ponto de interrogação e sinais de adição, têm significados especiais e devem ser precedidos por uma barra invertida caso seja necessário representar o próprio caractere. Sendo assim…

let er =  /tudo bem?/;

…não é o mesmo que

let er =  /tudo bem\?/;

Existe um site chamado Regex101 que permite testar e depurar on-line expressões regulares em PHP, PCRE, Python, Golang e JavaScript. Você pode utilizá-lo para testar os exemplos.

Mostre-me quais são meus poderes

Vamos entender os conceitos passo a passo por meio de dois exemplos, o primeiro através da validação de datas no padrão dia/mês/ano e o segundo validando e-mails.

Validando datas

Eis a expressão:

/^([0-2][0-9]|3[0-1])(\/)(0[0-9]|1[0-2])(\/)(\d{4})$/
Que horrível

É eu sei… mas você verá que não é tão complicado quanto parece.

Circunflexo ^

Faz parte dos chamados metacaracteres, isto é, caracteres que servem para representar outros caracteres, tais como: . ? * + ^ $ | [ ] { } ( ) \

Neste caso o “^” serve para indicar que a correspondência deve ocorrer no início da cadeia de caracteres. Por exemplo, se tivéssemos o seguinte:

^Asa

As correspondências poderiam ser:

  • Asa Branca
  • Asa-delta 
  • Asado

Lembrando que este metacaractere é case-sensitive, portanto “asa…” não seria uma correspondência válida.

Parênteses ( )

Indica início e fim de um grupo de caracteres, por exemplo em (hiper|hipo)termia as correspondências seriam:

  • hipertermia
  • hipotermia

Também é case-sensitive.

Lista […]

A lista guarda dentro de si os caracteres permitidos no texto pesquisado, então quando temos “[0-2][0-9]” estamos dizendo que queremos dois caracteres, onde o primeiro vai de 0 a 2 e o segundo de 0 a 9, como 22, 19, 11…. Poderíamos ter letras também como [a-z] (de a até z minúsculo) ou [A-z] (minúsculo ou maiúsculo).

Ou |

O ou, representado pela barra vertical |, nos fornece múltiplas alternativas, então se tivéssemos a expressão (pessoa|colina|topo) poderíamos obter as seguintes correspondências na frase abaixo:

Existe uma pessoa no topo da colina

Dígito e quantificador \d{n}

\d é uma classe/conjunto de caracteres predefinidos, que corresponde aos dígitos decimais de 0 a 9. Em seguida temos um quantificador que determina o número de vezes que queremos que esse dígito apareça, isto é, se colocarmos \d{4} é o mesmo que \d\d\d\d.

Cifrão $

Corresponde ao final da cadeia de caracteres de entrada.

Com isso já é possível analisar a expressão, começando pelo primeiro grupo /^([0-2][0-9]|3[0-1])… indicando o dia, e que traduzindo, ficaria algo como:

“Estou procurando uma data que comece com um grupo de caracteres onde o primeiro deve estar no intervalo de 0 a 2, o segundo no intervalo de 0 a 9 ou o primeiro deve ser 3 e o segundo deve ser 0 ou 1”

No segundo grupo (\/) dizemos que “o primeiro grupo deve ser seguido por uma barra”.

No terceiro grupo, designando o mês, (0[0-9]|1[0-2]), “deve haver um número começando com o valor 0 e terminando em um valor no intervalo de 0 a 9 ou um número começando com o valor 1 e terminando no intervalo de 0 a 2″.

No quarto grupo temos novamente a barra (\/).

E por fim terminamos com o ano, por meio do \d{4}, ou seja, “um número com quatro dígitos, seguido do $ indicando que aqui termina a cadeia que estamos considerando”.

Repare que essa expressão foi utilizada com teor didático portanto pode ser aprimorada, pois apesar de corresponder a várias datas válidas ainda considera algumas situações indesejadas, veja:

Correspondências válidas

31/12/2019, 20/01/1992, 02/02/2002, 31/12/0001, 00/12/0000, 00/00/0000

Validando e-mails

Eis a expressão:

/^[a-z0-9.]+@[a-z0-9]+\.[a-z]+(\.[a-z]+)?$/

A essa altura já não parece tão intimidadora quanto a anterior, não é mesmo?

Concordo

O primeiro intervalo [a-z0-9.]+ indica que podemos começar com uma cadeia contendo caracteres de a até z minúsculo, números de 0 a 9 ou o caractere “.”. O símbolo de adição diz que esse caractere deve se repetir uma ou mais vezes.

Em seguida temos o @ e mais um intervalo [a-z0-9]+ semelhante ao primeiro, porém sem o “.” e onde novamente os caracteres devem se repetir uma ou mais vezes.

Depois vem a barra invertida e o ponto “\.”, repare que sem a barra o ponto seria interpretado como um caractere qualquer.

Novamente outro intervalo de a até z [a-z]+ e ao final um grupo (\.[a-z]+) seguido de “?”. A interrogação nos diz que esse grupo de caracteres é opcional, ou seja, ele pode aparecer uma ou nenhuma vez, isso é útil em casos onde o e-mail termina em .com.br ou só em .com por exemplo.

Algumas das correspondências válidas são:

  • joaquim@gmail.com.br
  • ricardo.almeida@hotmail.com
  • maria.silva@yahoo.com

No entanto, mais uma vez é importante destacar o teor didático, pois novamente teremos situações indesejadas como a@b.c ou s@s.s.s.

Testando

JavaScript nos fornece vários métodos para trabalhar com expressões regulares, tais como test e exec do objeto RegExp ou match, search, replace e split do objeto String. Fiz um resumo do que cada um faz junto com mais alguns caracteres especiais que ainda não vimos.

test

Se queremos apenas testar se uma string de entrada corresponde a uma expressão regular utilizamos o método test que retornará um valor booleano.

let er = /^([0-2][0-9]|3[0-1])(\/)(0[0-9]|1[0-2])(\/)(\d{4})$/
console.log(er.test("20/10/2019")) // true
console.log(er.test("10/20/2019")) // false

exec

O método exec é útil quando você precisa trabalhar com os grupos ou índices capturados após a correspondência. O retorno será um array com o texto combinado na primeira posição e nas demais cada captura contendo o respectivo texto. Se não houver correspondência o retorno será null.

let er = /^([0-2][0-9]|3[0-1])(\/)(0[0-9]|1[0-2])(\/)(\d{4})$/

console.log(er.exec("20/10/2019")) // ["20/10/2019", "20", "/", "10", "/", "2019"]
console.log(er.exec("10/20/2019")) // null

match

O método match pesquisa correspondências em relação a uma expressão regular e as retorna como um objeto Array.

console.log('As Aventuras de Sherlock Holmes'.match(/[A-Z]/g)); // [ 'A', 'A', 'S', 'H' ]

“/g” é uma flag dizendo para encontrar todas as correspondências em vez de parar após achar a primeira.

search

O método search pesquisa por um valor especificado e retorna a posição da correspondência.

console.log("Anticonstitucionalismo possui 22 letras".search(/\s/)); // 22

“\s” equivale a um caractere de espaço em branco e o retorno é a posição 22, ou seja, a primeira ocorrência de espaço em branco, começando a contagem de caracteres em 0.

replace

O método replace retorna uma nova string com as correspondências substituídas por outra string.

console.log("Roberta foi ao Médico".replace(/médico/i, 'Parque')); // Roberta foi ao Parque

“/i” é uma flag dizendo para ignorar o fato de o caractere ser maiúsculo ou minúsculo.

split

O método split é usado para dividir uma string em substrings, por meio de um parâmetro que indica onde deve haver a separação.

console.log("Uva, maçã, peira e melancia".split(/,\s/)); // ['Uva', 'maçã', 'peira e melancia']

Conclusão

O assunto é extenso e ainda existem outros caracteres especiais, flags e quantificadores que não foram abordados, mas que não faltam boas referências para estudo, estarei indicando os sites MDN e Javascript.info que utilizei para consulta. Hoje ficamos por aqui, até o próximo artigo!

Até então

Compartilhe!