Regex para seres humanos 🤖
Olá ✌, nesse breve artigo quero trazer algumas pequenas coisas que me ajudaram e podem facilitar o seu entendimento no assunto... como não sou o dono da verdade, quero que você comente o que achou do post e suas sugestões de alterações!
Introdução
Regex (Expressões regulares) são amplamente utilizados para verificar, padronizar e até mesmo extrair uma parte de uma string. Ou seja, com ele podemos fazer 3 ações, buscar, validar e substituir
OBS.: Cada linguagem de programação lida com o regex de forma diferente, este breve artigo é mais voltado ao JavaScript
Sua Estrutura 🏛️
- Primeiro temos os separadores
/
,~
,@
,;
,%
, `,#
os separadores delimitam o início e o fim de um regex, dependendo da linguagem de programação devemos utilizar um ou outro, em algumas não devemos utilizá-los
\w
- Todos os caracteres dea-z
,A-Z
,0-9
e_
\W
- Tudo que não for letra nem número\d
- Número decimal0-9
\D
- Qualquer coisa que não seja um número\X
- Qualquer carácter unicode+
- Sequencia, como, por exemplo,\d+
é uma sequência de dígitos*
- Sequencia de caracteres ou nada|
- Ou[0-9]{4}
- Determina 4 dígitos de0
a9
[0-9]{2,4}
- Determina de 2 a 4 dígitos de0
a9
^
ou\A
- Início de string$
ou\Z
- Fim de string
- Após o fim do regex temos as flags:
-
g
Global - busca em toda a cadeia de caracteres o padrão desejado como é possível ver no exemplo a seguir:Regex: /a/g
a
ca
sa
éa
ma
rela
-
m
Multiline - usando-o os símbolos^
e$
correspondem respectivamente ao início e fim de cada linhaRegex: /^casa$/m
casa
é amarela, casa -
i
Insensitive - não diferencia letras maiúsculas com minúsculasRegex: /a/i
A
casa é amarelaRegex: /a/gi
A
ca
sa
éa
ma
rela
-
s
Single Line - faz a cadeia de caracteres ter tratada como se fosse em uma única linhaRegex: /a.*a/is
A
casa é amarela
Regex: /a.*a/i A c
asa é amarela
-
u
Unicode - os intervalos de [a-z] serão tratados todos os caracteres unicodesRegex: /\w/giu
A
casa
é
amarela
Regex: /\w/gi
A
casa
éamarela
-
Alguns exemplos de Regex 📑
-
Email:
/^[a-zA-Z0-9-.+]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
-
Slug:
/^[a-z0-9-]+$/
regex-23
-
Username:
/^[a-z0-9-_]+$/
pedro_06-2022
Mande o seu Regex que você mais utiliza e que sentiu falta, caso você queria se aprofundar mais no regex recomendo que leia e utilize a ferramenta regex101!
Muito bom, só gostaria de acrescentar uns detalhes...
Acho importante falar que existem diferenças entre as linguagens, cada uma implementa regex de um jeito e o que vale pra uma pode não funcionar em outra. Claro que muitas coisas funcionam igual, mas tem várias diferenças também.
Por exemplo, os separadores (que também podem ser chamados de delimitadores) que vc citou valem pra PHP e Perl, mas em JavaScript só se usa as barras (/regex aqui/
). Em outras linguagens, como Java, Python e C#, se passa uma string contendo a regex, mas sem os delimitadores (ex: em Python se faz apenas re.search('[a-z]', texto)
- se usar re.search('/[a-z]/', texto)
, ele vai procurar pelas barras no texto).
Muitas linguagens suportam Unicode por padrão, então atalhos como \w
consideram todas as letras definidas pelo Unicode, o que inclui vários alfabetos como o japonês, coreano, árabe, cirílico, etc. E também considera os dígitos Unicode (não é só de 0 a 9, tem vários outros caracteres).
Em Python 3, por exemplo, o \w
já suporta Unicode por padrão. Em PHP precisa usar a flag u
, no JavaScript essa flag não adianta e precisa de Unicode property escapes (ex: /\p{L}/u
para letras), em Java a flag que habilita o modo Unicode é Pattern.UNICODE_CHARACTER_CLASS
, etc. Cada linguagem pode ou não suportar Unicode, que pode ou não ser o default, e pode ou não ter modos de configurar.
Um detalhe importante que muita gente esquece - inclusive eu - é que o \w
(minúsculo) também considera o caractere _
. Então \W
(maiúsculo) na verdade é tudo que não for letra, número, ou _
.
Quanto as flags, também tem diferenças. A flag g
, por exemplo, é coisa do JavaScript. Em outras linguagens o normal é procurar todas as ocorrências, enquanto outras fornecem opções diferentes para cada caso. Por exemplo, em Python usa-se match
ou search
para buscar uma ocorrência e findall
ou finditer
para buscar todas. Em PHP usa-se preg_match
para a primeira ocorrência e preg_match_all
para todas, em Java faz-se um loop no objeto Matcher
(controlando a quantidade de vezes, caso não queira todas) e assim por diante. Ou seja, nessas linguagens não precisa da flag g
.
E a forma de ligar as flags também muda. Nas linguagens que possuem delimitadores (JavaScript, PHP, Perl), coloca-se no final (/\w/img
por exemplo), em outras coloca-se como parâmetros (exemplo em Python: re.compile(r'\w', re.I)
para passar a flag case insensitive).
Já o \X
é mais do que "qualquer caractere Unicode". Na verdade ele corresponde a um grapheme cluster. De forma bem resumida, muitos caracteres na verdade são formados por vários code points que juntos representam uma coisa só. Um exemplo moderno são as sequências de emojis, mas muitos idiomas possuem caracteres que só podem ser representados por mais de um code point - leia aqui, aqui e aqui para mais informações.
O problema é que nem todas as engines suportam o \X
, e mesmo quando suportam, ele pode não reconhecer todos os grapheme clusters, pois depende da versão do Unicode suportada.
Quanto a ^
e $
, que indicam o início e fim da string, vale lembrar que muitas engines possuem a flag multiline. Quando habilitada, esses atalhos também pegam o início e fim de uma linha. Já o \A
e \Z
pegam só o início e fim da string, independente da flag estar habilitada.
E algumas engines também suportam o \z
, que tem diferença quando a string termina com uma quebra de linha (\Z
vai até a quebra de linha e não a inclui no match, \z
vai até o final e inclui a quebra de linha).
Sobre os exemplos, alguns comentários:
A regex de email considera que coisas como -.+++@2.3
são emails válidos.
Regex pra email, que não dê esses falsos positivos, é bem mais complicado do que parece. Claro que a regex em questão pega emails válidos, o problema é que também pega muitos inválidos...
Para o slug, a regex considera que uma string vazia é um slug válido (porque o *
significa "zero ou mais caracteres", então se não tiver nenhum também serve). Se não era essa a intenção, pode trocar por +
(um ou mais caracteres) ou então usar quantidades específicas de acordo com cada caso (ex: {5,}
para ter no mínimo 5 caracteres, {5,20}
para ter entre 5 e 20, etc).
E na regex do username, acho que faltou um quantificador. Da forma que está (^[a-z0-9-_]$
) ele pega somente um caractere.
Cara, a minha vida de dev mudou completamente depois que eu aprendi regex. Tive uma época em que trabalhei muito com arquivos TXT e regex me salvava de mais!! Só acho uma pena que nos cursos que fiz (Técnico e Bacharelado) não tive uma disciplina dedicada a elas, tive que aprender sozinho!
Esse livro aqui me ajudou de mais!!
perfeito !!
Wow! leve embora minhas tabcoins! Até salvei aqui pra servir se consulta. Regex é aquele negocio que voce faz varias vezes, mas sempre esquece quando tem que fazer de novo rs
Como que a gente ainda não tem um botão "salvar" para poder deixar esse conteúdo guardado pra sempre?
Vou ver se já tiveram essa ideia no repositório.
É por isso que eu amo o chatGPT
Outra dica: ChatGPT tem ajudado bastante com regex ultimamente