Escolar Documentos
Profissional Documentos
Cultura Documentos
Expressão Regular é uma das ferramentas mais úteis que você pode ter. Vira e mexe as Expressões Regulares (RegExp) resolvem
desde problemas de Find & Replace no editor até validação de dados em diversos níveis do seu projeto. Mas geralmente a gente só lê
sobre Expressões Regulares quando precisamos decifrar aquela linha maluca e ainda assim de um jeito meio descuidado, tateando e
tentando fazer dar certo uma combinação de caracteres sem sentido.
Entendendo as Expressões
Uma Expressão Regular é uma representação para que você encontre padrões em um texto. Esse texto pode ser qualquer coisa, desde o
valor de um campo de formulário ou simplesmente um search no seu editor de código predileto… Não importa… O objetivo é filtrar
padrões em um punhado de informação textual.
Se você entender que uma Expressão Regular é apenas uma representação formada por símbolos, você não vai ter dificuldades. Cada
símbolo representa um tipo de informação. Por exemplo: o . (ponto) é um curinga. Ele significa que você pode selecionar qualquer
caractere, ou seja, qualquer letra, caractere especial ou número. Exceto a quebra de linha, que é representado pelo símbolo \n.
Classe de caracteres
Vamos começar pelo mais fácil: quando você faz uma busca, você pode buscar uma combinação de caracteres específica, por exemplo:
no seu editor de código, se você fizer uma busca por a, ele vai te mostrar todas as letras a do documento. Mas e se você quiser procurar
todas as letras a e as letras e? Simples, você faz um agrupamento utilizando os colchetes []. Essa expressão irá encontrar todos os
caracteres que estiverem dentro dos colchetes. Veja esse exemplo, onde ele filtra as letras [ue]. Isso se chama classe de caracteres,
onde você encontra vários caracteres diferentes ao mesmo tempo.
Bom, se você quiser selecionar TODAS as letras do texto, você não precisa escrever o alfabeto inteiro dentro dos colchetes, basta só
usar a representação [A-z]. Isso quer dizer que ele pega as letras de A até Z, maiúsculas ou minúsculas.
Se você quiser pegar os números, por exemplo, use [0-9]. Se quiser todas as letras e todos números: [A-z0-9]. Pra facilitar a
expressão, você pode usar \w, que vai dar no mesmo.
Para você fazer uma negação da Classe criada, basta adicionar um ^ dentro da classe. Por exemplo, você quer pegar todas as
combinações que não sejam formadas pela sequência es: [^es]. Veja esse exemplo aqui.
A classe \w recupera todos os caracteres alpha numericos, ou seja, letras e números, mas não acentos ou caracteres especiais. É o
equivalente a [a-zA-Z_0-9]
A classe \W pega TODOS os caracteres que não seja alpha numericos, ou seja, pontuações e espaços.
A classe \s é o equivalente [ \t\n\x0B\f\r]
A classe \d é o equivalente [0-9]
E as classses de negação. Lembrando que basta colocar o sinal de ^ logo depois do colchete inicial [:
Múltiplos padrões
Imagine agora que você queira encontrar dois padrões diferentes de caracteres, por exemplo, duas palavras. Bastando usar o símbolo |
(pipe), que vai significar OU. Nesse caso a expressão irá reconhecer um ou o outro padrão. Veja esse exemplo onde recuperamos o
retorno das palavras dolor ou labore.
Âncoras
As âncoras recuperam a posição entre os caracteres, mas não os caracteres em si. Por exemplo, a expressão ^dolor, vai recuperar as
palavras dolor que estiverem no início da linha (veja o exemplo). A expressão dolor$ vai recuperar o termo que estiver no final da
linha (veja o exemplo).
Modos
Agora, suponha que você queira pegar uma sequência que contenha um termo parecido, mas que possa estar com algumas letras
maiúsculas ou minúsculas. Por exemplo os termos Lorem, lorem, loRem, lOrEm etc, bastaria usar a representação (?i) antes do
termo a ser buscada. A expressão ficaria assim (?i)lorem. Veja este exemplo aqui.
Uma tarefa muito corriqueira é a validação de campos de e-mail. Sem entrar nas polêmicas (validar essas coisas sempre é chato), mas é
legal para treinar o que você acabou de ler. A expressão para fazer a validação é essa:
^\w*(\.\w*)?@\w*\.[a-z]+(\.[a-z]+)?$
Explicando:
Existem alguns sites pra facilitar a criação e o debug das expressões regulares, veja abaixo:
https://regex101.com/r/vS7vZ3/224#javascript
https://rubular.com
https://aprenda.vidageek.net/aprenda/regex
https://turing.com.br/material/regex/introducao.html#
https://msdn.microsoft.com/pt-br/library/az24scfc(v=vs.110).aspx
Expressões Regulares
Apresentação
Fala pessoal, tudo certo? Meu nome é Raul, tenho 24 anos e sou desenvolvedor há quase 3 anos. Comecei a
programar na faculdade com Java, PHP, até me deparar com o fascinante mundo do client-side e resolver
abandonar um pouco o server-side pra entender a mágica da força trindade HTML, CSS e Javascript.
Há pouco mais de uma semana eu me deparei com um problema que todo desenvolvedor já se deparou — e
se ainda não, eu garanto que vai — que é encontrar um padrão textual em uma massa de dados. No caso, era
pra fazer uma leitura de um código HTML inteiro e filtrar determinadas informações. Essa prática é
chamada de Web Scraping.
Fazer isso manualmente apesar de trabalhoso, é fácil, afinal, é da natureza do ser humano buscar padrões
em todo lugar. Mas e programaticamente? Bom, aí as coisas começam a ficar um tanto complicadas, afinal,
são muitas variáveis como: “pode ou não vir uma classe”, “pode ou não ter id” e etc. É aí que a gente se
depara com as tão temidas Expressões Regulares.
Voltando ao meu problema, tentei fazer de diversas formas (mesmo sem saber direito o que eu estava
fazendo).
Então eu decidi que eu iria estudar pra valer e aprender de uma vez por todas isso. E como é como dizem:
1. Introdução (esse)
2. Metacharacters
3. Classes de caracteres
4. Quantifiers
5. Capturando Grupos
6. Âncoras
7. Regex no mundo JavaScript
Espero que vocês gostem da série e passem a utilizar no dia-a-dia de vocês sem precisar consultar no Stack
Overflow! :p
Expressão Regular é uma linguagem de busca de padrões. Resumindo bem a opera, é uma linguagem onde
dizemos o padrão (pattern) do texto que queremos encontrar, passamos o texto alvo (target) e pedimos para
uma Regex engine (motor) fazer essa busca. É como se fosse o famoso ctrl+f, só que muito mais profundo
e detalhado.
Fluxo de expressão regular.
Como mencionado, para que seja possível realizar uma busca utilizando Regex, é necessário uma Engine,
ou seja, um motor que tem a finalidade de avaliar o contexto e fazer a busca.
Cada linguagem de programação tem o seu motor e apesar de Regex ter um padrão fixo, há algumas
diferenças e detalhes na implementação em cada uma. Mas o que vai mudar de fato é a forma com que se
trabalha, e não a lógica e os padrões em si.
Bom, vamos ver como funciona esse tal de Regex na prática pra ficar mais fácil.
Digamos que eu tenho um arquivo CSV com milhares de linhas com informações de pessoas, contendo na
sequência: Nome; Endereço; Cep e Telefone.
Então, queremos pegar apenas o CEP de todas as linhas de nosso arquivo. A forma mais fácil (não a mais
otimizada) de fazer isso seria:
\d\d\d\d\d-\d\d\d
Mas o que?
O \d é uma classe de carácter (explicarei no próximo artigo), onde ele vale números de zero a nove (0–9).
Logo, sabemos que todo CEP possui 5 números, um hífen (-) e mais 3 números. Logo, temos:
No mundo da Regex, o ponto de interrogação tem vários super poderes e veremos todos eles durante a série.
Mas, um deles é o papel de quantifier, ou seja, ele é um atalho (short hand) que diz que um elemento pode
ou não aparecer na expressão.
Logo, para considerarmos o caso onde o hífen pode ou não vir, mudamos a nossa expressão para:
\d\d\d\d\d-?\d\d\d
Capturando CEP com e sem hífen!
Conclusão
Como eu comentei anteriormente, esse é apenas um exemplo bem rústico e simples do uso de expressões
regulares. Elas podem ficar muito mais elegante e fácil de ler e também muito mais complexas.
Caso queira testar você mesmo, pode utilizar o Regex101, um site que possui diferentes Regex Engine e até
gera o código para a linguagem, dado o target e o pattern.
C ontinuando a saga mágica das Expressões Regulares, hoje vamos entender um pouco mais do que se
treta meta-caracteres no mundo da Regex! Mas antes, se você ainda não viu a parte um, clica nesse link e da
uma conferida!
Metacharacter
Metacharacter ou Meta-caractere é todo caractere que não possui o seu valor semântico propriamente dito,
ou seja, visualmente ele parece uma coisa, mas no fim ele tem um significado especial! — Hãm?
Quando você vê o caractere ponto (.) em um texto, qual é o significado dele? Bom, num texto normal
provavelmente ele tem o valor de ponto final, não é? Na mosca, jovem! Entretanto, esse é o valor semântico
associado ao português. No mundo das expressões regulares a história é um pouco diferente. Vamos dar
uma olhada na prática para elucidar melhor.
Sabendo que se quisermos selecionar um caractere específico em Regex, podemos passar ele diretamente na
expressão. Logo, vamos tentar fazer isso com o ponto:
90 matches
90 MATCHES?
Pois é. Em uma Expressão Regular, um ponto (.) tem o valor de qualquer caractere, ou seja, o que ele diz
pra engine é: “Pega mim TUDO que estiver no target”.
Empolgante!
Há mais alguns caracteres que possuem esse mesmo comportamento, a maior parte deles são quantifiers, e
isso a gente vai ver futuramente.
Outros tipos de meta
Além do metacharacter citado, ainda temos um outro tipo. Esse é o inverso do anterior, ou seja, é quando o
caractere tem seu valor literal interpretado normalmente pela Engine, mas quando colocamos uma barra
invertida (\), concedemos super poderes à eles.
Números
Lembra do exemplo do artigo 1, onde usamos \d ? Então, ele é um exemplo muito fácil disso. Somente o d ,
é interpretado como a letra d. Mas usando a barra invertida, transformamos ele num “pegador de caracteres
alfanuméricos”, veja no exemplo a seguir:
Desse tipo, temos uma série que será extremamente útil, vamos dar uma olhada com mais calma em cada
um deles.
Espaços em Branco
Lembro uma vez onde na empresa que eu trabalhava, a gente teve um problema com uma integração de um
arquivo. Depois de muito quebrar cabeça, conseguiram descobrir que o software do cliente estava inserindo
um caractere em branco que, segundo a tabela UNICODE, era um outro tipo de espaço em branco. E pelo
fato de vir no meio do texto, um método trim() não resolvia.
No caso em questão, o pessoal não usou Regex, foram bem cangaceiros e conseguiram encontrar na raça!
Mas, talvez tivesse sido um pouco mais fácil utilizando Regex, não?
Para esses casos, temos um metacharacter especial que é o \s ! O que ele faz é considerar qualquer espaço
em branco, seja ele um espaço, um tab, uma nova linha, um form feed, e até mesmo um vertical tab (nem
sabia que existia tanto caractere para espaçamento).
Hands on!
Poderíamos tentar pegar um por um, mas vamos ser espertos e pegar todos sem erro:
Pegando todos os white spaces!
Então fica a regra: “Precisa considerar espaços em branco na sua expressão? \s”.
Word Characters
Word Chacaraters, ou caracteres de palavras (tradução livre), é o cara que vai nos ajudar a pegar todas as
letras, números e underscore (underline).
Uma coisa que precisa ser mencionada e ser gravada no âmago da memória de vocês desde já é:
Acredito que quem está lendo sobre expressões regulares já sabe o que é esse termo, mas caso você ainda
tenha algum tipo de dúvida, case sensitive é quando caracteres maiúsculos e minúsculos não possuem o
mesmo valor, ou seja, A é diferente de a, igual em senhas, sabe?
Quando queremos validar se um caractere é uma letra, podemos usar classes de caracteres (veremos no
próximo artigo), passando a seguinte expressão:
[A-Za-z]
No exemplo acima, falamos que esperamos uma letra que esteja dentro do alfabeto e pode ser maiúscula ou
minúscula. Mas vamos dizer que queremos pegar um nickname, e nele pode vir além de letras, números e
underline (_)?
[A-Za-z0-9_]
E é aqui que entra o \w ! A declaração acima, pode ser resumida apenas com \w. Duvida?
Expressão gigantesca
word character \w
Negando o meta-caractere
Legal esse monte de atalho pra gente usar, diz aí? Mas assim como eu me perguntei quando aprendi isso,
você também pode ter se perguntado:
Mas e se… eu quiser pegar qualquer caractere que NÃO for um número ou world character ou white space?
Para nossa alegria, existe uma forma bem simples de fazer essa negação. Nas linguagens de programação,
geralmente quando a gente quer negar uma condição booleana (true/false), apenas passamos uma
exclamação:
senhaRecebida == senha // A senha recebida é igual a senha?
senhaRecebida != senha // A senha recebida é diferente da senha?
Para fazermos isso, com esses 3 meta-caracteres que vimos, basta colocar a letra maiúscula! Sim, simples
assim!
Essa lógica vai ser aplicada pra outros meta-caracteres que veremos mais a frente. Logo, lembre-se dela! :D
Conclusão
Esse artigo foi bem teórico apesar dos exemplos, mas é extremamente importante que você compreenda o
que são os meta-caracteres e como eles são entendidos, pois, nos tópicos seguintes eles serão
constantemente utilizados.
Espero que tenham gostado! Até o próximo! =D
Regex
Fala galera, belezinha? Hoje o nosso bate-papo vai ser sobre uma das principais (e mais útil)
funcionalidades das expressões regulares, classes de caracteres.
Espero que estejam gostando da série e caso você seja novo por aqui, da uma olhada na parte 1, clicando
aqui.
Sem delongas, liga teu Pomodoro, pega teu café/chá/chimarrão, me dê a mão (me abraça) e vem comigo!
Suponhamos que queremos analisar (de uma forma pouco grosseira) se uma data é valida ou não. Sabemos
que no formato PT_BR, ela segue o padrão dia/mês/ano (28/05/2017).
Esse padrão segue algumas regras básicas, por exemplo, o dia só pode começar com zero (0), um(1), dois(2)
ou três(3). Então, como podemos fazemos então para dizer à Regex que o primeiro número precisa ser um
desses 4 dígitos? Se você respondeu com classes de caracteres, parabéns! :D
Colchetes
O símbolo de colchetes ([]) são usados para indicar quais os caracteres queremos considerar. Voltando no
exemplo anterior, para expressar que queremos apenas os números 0, 1, 2 e 3, podemos fazer:
[0123]
Hífen mágico
Outra forma de escrever a mesma coisa do exemplo acima, de forma bem bacanuda e mais sucinta, é
utilizando o hífen (-).
Com ele, definimos um um range (alcance), ou seja, um valor inicial e um valor final, que no caso serve
para letras e números. Melhorando a expressão anterior, temos:
[0-3]
Ponto de atenção!
Quando comecei a estudar sobre Regex, me deparei com uma observação sobre o uso hífen dentro de
classes que me intrigou bastante.
Fora da classe o hífen é avaliado como hífen mesmo (valor semântico), mas como dito anteriormente,
dentro ele é avaliado como um range. Assim, suponhamos que você queira considerar um hífen, ou ponto-
e-virgula, ou exclamação em um trecho de texto. Para tal, podemos utilizar classe de caractere:
Character range is out of order!
Bem, o que a engine faz é tentar encontrar o range entre o ponto-e-virgula (;) e a exclamação (!). E como
isso não faz sentido, o erro acontece.
Lembra do post passado sobre meta-caracteres? Sim, o hífen é um deles! E para resolver o caso, precisamos
apenas passar a nossa amiga barra invertida (\):
Pegando o hífen!
Shorthand
A segunda maneira de filtrar uma classe de caracteres é utilizando uma forma abreviada (shorthand) da
própria linguagem.
Números
Em muitos casos, desejamos pegar números de zero (0) a nove (9). Logo, podemos fazer utilizando o hífen:
Utilizando hífen
Mas podemos simplificar ainda mais utilizando o shorthand \d que tem exatamente o mesmo significado
(números de 0 a 9):
Utilizando shorthand
Letras e números
No caso das letras, podemos definir na classe que queremos de aaté z maiúsculo ou minúsculo e números de
0a 9 , assim, usando hífen ficaria:
O shorthand para esse mesmo valor seria o \w , mas com uma ressalva: ele considera underscore (_)
também:
Shorthand para números e letras
Qualquer caractere
Uma classe MUITO útil é o ponto ( . ). O ponto representa QUALQUER caractere, e quando eu digo
qualquer, é realmente qualquer (espaço, números, letras, etc.)
HANDS ON!
Bora codar!
Licença didática: Antes de mais nada, gostaria de deixar claro que a validação que faremos a seguir é bem
grosseira, ou seja, ela deixará brechas para uma data inválida, como 20/19/2999 (mês 19???). Assim, não
validem se ela é uma data válida (de acordo com o calendário) dessa maneira. =)
Com todo esse conceito novo em mente, vamos atacar as datas então.
Eu não sei vocês, mas eu particularmente quando vejo uma expressão regular, fico meio confuso. Então, pra
entender melhor, eu vou imaginando ela por blocos e no fim, junto todas as partes para entender o que ela
faz em si.
Dia
Para finalizar, vem uma barra ao final dessa expressão. A barra é um meta-caractere, logo,
precisamos escapar usando barra invertida:
[0-3]?\d\/
Dia: done!
Mês
[0-1]?\d
[0-1]?\d\/
Queremos que o seja seja 1000 e 2000, logo, o primeiro digito será entre 1 e 2:
[12]\d\d\d
Mesmo resultado!
Antes de fechar o assunto, queria comentar um pouco a respeito sobre as letras e os caracteres especiais.
Lembra quando eu disse que a classe \w pega letras? Vamos tentar selecionar todas as letras da palavra
maçã:
Ué
Pois é, tanto o \w quanto o a-zA-Z levam em consideração apenas o alfabeto, ou seja, a até z. Como a língua
portuguesa é uma (p…) lindeza, e temos diversos símbolos como til (~), cedilha (ç) e etc, sempre que
quisermos validar textos que contenham esse tipo de informação, precisamos explicita-los dentro de uma
classe:
Considerando caracteres especiais
Conclusão
Classe de caracteres será sem dúvida, uma das coisas que você mais vai utilizar no uso das expressões
regulares. Portanto, vale um ponto de atenção e dedicação para entender de verdade como elas funcionam!
Espero que tenham gostado! Caso tenham alguma dúvida ou sugestão, podem me mandar mensagem que eu
terei o prazer de responder! \o
Entendendo de uma vez por todas Expressões Regulares: Parte 4— Quantifiers
“My precisous!” — Gollum
F ala galere, tudo em cima? Hoje é dia de mais um artigo sobre expressões regulares e hoje iremos
abordar os quantifiers (quantificadores), ou seja, formas de definir quantas vezes queremos que elementos
apareçam ou não em nossas expressões.
Lembrando: caso você seja novo por aqui, acesse o primeiro post.
Como dito no começo do artigo, quantifier é uma forma de você dizer à Regex engine quantas vezes você
quer que aquele caractere apareça. No primeiro exemplo do primeiro post (link aqui) eu citei um caso onde
era necessário validar um CEP:
//Target
João da Silva;Rua Cabloco terceiro, 25;11111-111;(99)9999-9999
Márcio Cunha;Rua João Gourlat, 150;12123111;(99)8888-9999
5 dígitos iniciais;
Um hífen (que pode não vir);
3 dígitos finais.
\d\d\d\d\d-?\d\d\d
Resultando em:
aeHOOo
Porém, dessa maneira é um pouco estranha né? Repetimos a mesma condição (\d ) 5 vezes, depois mais 3
vezes. Mas, como deixar isso um pouco mais bonito? Sim, quantifiers.
Declarações
Existem algumas maneiras de declarar um quantifier, cada um, atendendo a um propósito em específico.
Nas maneiras a seguir, sempre que você ler “n” ou “m”, entenda como números inteiros quaisquer
(matemática feelings?).
Apareça “n” vezes
Quando queremos que um determinado caractere apareça especificamente “n” vezes, passamos o número
de vezes (n) entre chaves, após alguma declaração. Lembra do caso do CEP? Como sabemos exatamente
quantas vezes cada número deve aparecer, podemos mudar a nossa expressão de:
\d\d\d\d\d-?\d\d\d
Para
\d{5}-?\d{3}
{n,}
Agora para ver na prática, vamos imaginar que queremos avaliar um texto onde deverá vir no mínimo 4
letras seguido de no mínimo 8 dígitos:
//Target
regex1234567
reg12345678
regex12345678
regex123456789012
Como sempre digo, resolver a expressão por partes, teremos para a primeira parte (mínimo 4 letras
[considerando maiúscula e minúscula]) a seguinte expressão:
[A-Za-z]{4,}
\d{8,}
Resultado da expressão.
Perceba a primeira linha, não possuímos no mínimo 8 dígitos, logo, ela é desconsiderada. Na segunda linha,
temos os 8 dígitos mas não temos no mínimo 4 caracteres! Já na terceira e quarta linha, temos a nossa
condição totalmente atendida, uma vez que é no mínimo, ou seja, “n” ou mais!
Apareça no mínimo “n” e no máximo “m”
Mas e nos casos onde queremos definir o mínimo e o máximo? Bom, para esses, ainda utilizaremos chaves,
mas passaremos o número mínimo “n”, uma virgula, e o número máximo “m”:
{n,m}
^[a-z0-9_-]{3,16}$
Obs.: Não se preocupe com o ^ e o $ , são âncoras e veremos nos posts a seguir. Apenas tenha em mente
que eles delimitam um começo e fim na expressão, ou seja, o texto deve corresponder exatamente aquela
expressão.
Aplicando a expressão em alguns possíveis usuários, temos o resultado:
Lembra da Regex do CEP? Podemos ou não receber um hífen da máscara. Para resolver isso, utilizamos
o?:
\d{5}-?\d{3}
Vale ressaltar que esse caso tem o valor de zero (0) vezes ou uma (1) vez. Seria equivalente o “true” ou
“false”.
*
Pelo menos 1 vez
Em casos onde queremos filtrar no mínimo uma vez, e não temos restrições máximas, podemos utilizar por
chaves:
{1,}
Para pegar as letras e espaços, utilizaremos uma classe de caractere (caso não tenha visto ainda o post sobre,
clica aqui) para considerar as letras e os espaços:
[A-Za-z\s]
Ainda não!
Perceba que selecionamos as letras maiúsculas e minúsculas e os espaços em branco. Entretanto, foi
selecionado cada letra e espaço individualmente. No fim, queremos que seja selecionado os blocos inteiros
que corresponderão aos nomes, correto? Então é aqui que entra o quantifier +:
[A-Za-z\s]+
éééééé….
Fazendo uma pequena retrospectiva na aula sobre classes, quando definimos que queremos “word
characters”, a regra só vale de a até z, e não temos no alfabeto cedilha, tio no a e etc. Mas para resolver
isso, é só explicita-los:
Ae caramba! =D
Finalmente! (y)
Quantifiers são gananciosos!
É preciso tomar um certo cuidado com quantifiers! Por padrão, eles possuem um comportamento greedy
(adj. Ganancioso). Mas, o que isso significa na prática?
Lembra do exemplo dos nomes completos? O quantifier + (e todos os outros) entende da seguinte forma:
“Vou selecionar tudo o corresponder à essa expressão, até encontrar algo que não responda, aí eu paro!” —
quantifier
E é exatamente isso que ele fez. Ele selecionou TUDO que era letras e espaços em branco, até se deparar
com o ponto-e-virgula e ser obrigado a parar a seleção. Meio abstrato né? O que ele faz é isso aqui:
Comportamento greedy
Mas Raul, como evitar esse comportamento? Eu quero meu quantifier preguiçoso!
Sempre que você quiser evitar esse comportamento — quiser que ele seja preguiçoso — após a declaração do
quantifier, basta passar uma interrogação. Assim, discurso dele vai passar para:
“Vou selecionar o texto até acontecer a primeira ocorrência correspondente à essa expressão!” — quantifier
Difícil imaginar um caso pra evitar esse comportamento? Vamos observar o target abaixo:
<h1 class="header" id="meu-titulo">Minha página</h1>
Por algum motivo obscuro, desejamos selecionar a tag h1 inteira, com seus atributos e tudo mais. Como
poderíamos resolver isso?
<h1.+>
Por favor, regex engine, peguei os elementos que comece com <h1 tudo o que tiver, quantas vezes forem
necessários(.+) e pare até encontrar > .
Checkando o resultado:
Ainda não!
Não cara.. pior que não!
Pois é. Dessa maneira, ele pega a última ocorrência do sinal de maior (>). Mudando o quantifier de
ganancioso para preguiçoso com a interrogação, temos:
Aleluia!
Conclusão
Espero que tenha ficado claro o papel dos quantifiers e como eles serão úteis nas vossas expressões. Use e
abuse delas, principalmente das mais curtas (*,+,…).
Até a próxima!
F ilhos de Zion, tudo na paz? Continuando nossa saga nas Expressões Regulares, hoje iremos abordar
uma funcionalidade que começa a dar vida e trazer uma real utilidade para as nossas expressões: Grupos de
caracteres.
Então, liga teu white noise e teu pomodoro e vamo que vamo!
Grupos de Caracteres: o que são?
Grupos de caracteres em expressões regulares são formas de agrupar expressões. Com eles, podemos
selecionar o que queremos ou não receber nos resultados.
Até agora, estamos definindo expressões sem grupo e quando temos um match, é no resultado inteiro da
expressão. Mas… e se quisermos pegar só um pedaço daquela expressão? Só uma parte em específico e o
resto podemos até descartar?
<h1>Meu Titulo</h1>
Vamos imaginar que queremos pegar o valor do título entre as tags. Assim, podemos pensar da seguinte
forma:
Resultado:
<h1>[\w\s]+<\/h1>
Full match!
Mas, não era bem o que a gente queria né? Queremos só o conteúdo do titulo e não as tags que ele está
inserido.
Utilizando os grupos
Para definir um grupo de caracteres, basta escrever uma expressão entre parênteses (“( )”). Dessa maneira,
começaremos a receber como resultado os valores do grupo, não só o full match como estamos
acostumados.
Continuando no exemplo do título, vamos separar cada bloco de código por grupos:
1. <h1> = (<h1>)
2. [\w\s]+ = ([\w\s]+)
3. <\/h1> = (<\/h1>)
Resultando em:
(<h1>)([\w\s]+)(<\/h1>)
Viu só? Agora além de recebermos o valor full match da expressão, recebemos também valores de cada
grupo que definimos. E é aqui que a coisa começa a ficar interessante pra valer!
Mas Raul, a gente não queria somente o valor do título? Por que tá vindo as tags ainda?
Como vimos no nosso exemplo, agora estamos pegando os valores dos grupos. Mas como solicitar à Regex
Engine para que NÃO traga o valor de algum grupo? Para faze-lo, basta no início do grupo digitar (?:) .
(?:<h1>)([\w\s]+)(?:<\/h1>)
Finalmente! =D
Como escapamos o valor dos grupos das tags, agora só temos o full match e o resultado do conteúdo da
nossa tag h1.
Diferentes cenários
Imagine que além de validar a tag h1, queremos considerar todos os tipos de titulo (de 1 a 6).
Bem, já sabemos fazer isso né? Basta utilizar uma classe de caractere, passando que queremos de 1 até 6:
<h[1-6]>
(?:<h[1-6]>)([\w\s]+)(?:<\/h[1-6]>)
Resultando em:
Bem loco!
Todos nós sabemos que a tag de fechamento deve ser a mesma de abertura. Caso não seja, é um elemento
inválido! Entretanto, olha o resultado da nossa expressão:
õ.Ô
Chega…
Backreferences
Backreference é uma forma de fazer referência à um grupo que já foi informado anteriormente, ou seja, é
uma forma de dizer à engine que queremos fazer referência (ter o mesmo valor aqui) do grupo “n”.
Número do grupo
Cada grupo que definimos ganha um número, que no caso seria sua posição na expressão. Esse número,
varia conforme a ordem de declaração, ou seja, o primeiro grupo ganha o número 1, o segundo ganha o
número 2 e assim sucessivamente.
Ordem dos grupos
Vale deixar claro que o que determina a ordem e os números de cada grupo é a ordem de precedência, ou
seja, se tivermos grupos dentro de grupos, a regex irá definir todas os grupos daquele aninhamento e depois
continuará para os outros que estão fora:
aninhamento de grupos
Fazendo uma analogia, lembra das aulas de matemática, onde temos várias expressões aninhadas e
precisamos resolver de fora pra dentro? É a mesma coisa:
ATENÇÃO
Vale comentar um comportamento que pode fazer a gente quebrar muito a cabeça e acreditar que estamos
fazendo algo errado.
Quando definimos que o grupo será “non-capturing” (?:), além dele não aparecer nos resultados, ele
também não ganha uma numeração. Ou seja, você não conseguirá fazer referências à ele. =/
Voltando pra resolver o último exemplo, queremos que quando a tag de abertura for h1, a tag de
fechamento também seja h1. Com o conceito de backrefence em mente, definiremos que o h[1-6] :
(?:<(h[1-6])>)
Agora, faremos a referência a ele. Como na ordem de precedência ele é o grupo número 1 (devido ao fato
de termos negado o pai), declaramos nosso que o backreference é igual a \1 :
([\w\s]+)(?:<\/\1>)
(?:<(h[1-6])>)([\w\s]+)(?:<\/\1>)
Resultando em:
Perceba que agora blindamos a nossa regex contra um comportamento estranho. Infelizmente recebemos
um grupo não desejado, mas é o preço da referência por números.
De qualquer forma, nosso objetivo foi alcançado!
Yeah!
Conclusão
Ainda há mais algumas formas de fazer referência, como por exemplo por nome. Entretanto, essa
funcionalidade não é comum em todas as engines.
Grupos nos permitem dar uma complexidade e flexibilidade às nossas expressões. Assim, tenha em mente:
Sempre que precisar pegar algum resultado específico, utilize grupos!
F ala meu povo, tudo bem com vocês? Estamos quase fechando a série “Entendendo de uma vez por
todas Expressões regulares”.
Hoje iremos abordar um tema que pra mim, é um dos mais difíceis no que tange à Regex: Âncoras. Mas
antes, se você acabou de chegar por aqui e quer acompanhar desde o primeiro tópico, sugiro acessar o
primeiro artigo (introdução).
Um pouco abstrato né? Vamos olhar esta string de teste bem simples:
a{3}
Perceba que filtramos 3 “a” até do segundo conjunto, onde há 4. Mas pegamos apenas 3. E é exatamente
nos casos onde não queremos que essa correspondência ocorra que usamos as âncoras.
Tipos de âncoras
Existem algumas formas de definir âncoras, mas independentemente de qual o tipo, elas sempre terão um
mesmo padrão: âncora inicio + expressão + âncora fim .
Word boundary
Esse tipo de âncora é definido pela expressão \b . Ela indica que você deseja buscar uma expressão que não
comece e nem termine com letras.
Voltando no nosso exemplo simbólico, podemos usar esse tipo de âncora para resolver o problema:
Perceba que agora temos exatamente as correspondências que queríamos, pois, agora que definimos que a
expressão não pode terminar com uma letra, ele descarta o caso aaaa .
O inverso
Como já comentei nos tópicos anteriores, em Regex, geralmente quando queremos fazer uma negação,
usamos a letra maiúscula, ou seja, se eu digo que não quero que minha regex comece ou termine com letras
e números usamos \b , mas para dizer o contrário — que sim, queremos — basta utilizarmos o \B .
Perceba que agora definimos que a nossa expressão PODE começar com um caractere, mas não pode
terminar, tanto isso é verdade que o ultimo conjunto ( aaaab ) não é selecionado.
Início e Fim
Ainda temos âncoras que definem um início e um fim em nossas expressões, ou seja, o target (string alvo)
avaliado deve começar e terminar daquela forma, caso contrário, não teremos um match.
Esse tipo de âncora é definido por ^ e por $ , sendo o primeiro usado no início da expressão e o segundo no
fim:
^<minha-expressão>$
Tenho uma aplicação que precisa fazer uma busca em uma pasta cheios de arquivos de extensão TXT, e
queremos filtrar um padrão especifico de nomenclatura dos arquivos. Digamos que essa nomenclatura seria
algo:
ano-nome_do_arquivo.txt
Quando fazemos a leitura dos arquivos na pasta, receberemos uma lista similar a essa:
Target:
2017-index_produto.txt
produtos.txt
2017-index_clientes.txt
clientes.txt
2016-users.txt
Bom, como eu sempre gosto de fazer, vamos montar a regex por partes:
\d{4}-\w+.txt
E como resultado:
Entretanto, temos uma pequena falha nessa abordagem. Digamos que um arquivo com extensão .txts seja
colocado na pasta. Na hora de buscar os nomes dos arquivos, pegaríamos ele também:
Deu ruim!
Bom, não queremos então que os nossos matches comecem e terminem dessa maneira? Nem mais nem
menos? Então vamos usar âncoras de início e fim:
^\d{4}-\w+.txt$
Ih rapaz…
Eita… Calma.
Comportamento
Como eu disse anteriormente, o comportamento da âncora de início(^) e fim($) é definir que não teremos
MAIS NADA antes e nem depois, mas, implicitamente, o nosso alvo avaliado (string de nomes de arquivos)
possui uma quebra de linhas e esse é o motivo de simplesmente não ter funcionado.
Infelizmente, cada engine (motor) trata essa quebra de linhas de uma forma diferente. Caso você não seja da
turma do Javascript, sugiro dar uma olhada como a engine da sua linguagem favorita trata isso.
Além da nossa expressão, podemos passar flags que são basicamente configurações no modus operandi do
moto da Regex. A flag /g indica que essa expressão pode se repetir várias vezes, ou seja, ela terá o
comportamento de encontrar todas as correspondências no nosso alvo. Já a flag /m indica que queremos
considerar quebras de linhas.
No mundo Javascript, para aplicar essas flags você deve definir no fim da expressão. Entrarei em mais
detalhes no próximo artigo.
Por padrão, já estamos usando o /g , e agora combinaremos o /m para consertar nosso exemplo anterior:
Caso tenha alguma dúvida ou sugestão de correção, fique à vontade para comentar aqui ou interagir comigo
das redes sociais.
F ala meu povo, tudo bem com vocês? Seguindo no penúltimo artigo da série regex, hoje falaremos
(finalmente) sobre como utilizar as expressões regulares no mundo JavaScript.
Mas antes de tudo, se você chegou direto nesse tópico e ainda não tem tanta familiaridade, da uma olhada
no post onde tudo começou.
Declaração
Como a maioria dos objetos JavaScript, temos duas maneiras de declarar uma expressão regular, invocando
o construtor ou de maneira literal.
Constructor
A primeira forma (e menos usada), é chamando o construtor do objeto RegExp:
O primeiro argumento que o objeto recebe (nosso padrão), pode ser qualquer coisa (JavaScript feelings).
Porém, para o bom uso, ele deve ser uma string ou uma outra regex (sim, outra regex).
No caso da String, é necessário entender que as vezes precisaremos escapar os caracteres que possam ter
outro significado. Quem aqui nunca usou um \n pra dar aquela quebra de linha?
Então, se quisermos considerar que a barra seja de fato uma barra, e não um "efeito especial", precisamos
escapa-la com outra barra (._.):
O segundo parâmetro é a flag que adiciona um comportamento em nosso motor da Regex. A mais popular e
já comentada bastante é a flag g , que permite que o motor continue procurando por todo o alvo o seu
padrão. Mas também existe outras bem importantes, como:
Esses são as flags que o JavaScript aceita, mas se você é de outra linguagem, talvez tenha mais tipos
diferentes.
Passando então a nossa expressão e a flag, temos uma declaração semelhante a essa:
Literal
A segunda forma de declarar uma expressão regular é fazendo da forma literal, ou seja, passando uma
expressão entre duas barras / e em seguida, passando as flags:
Alvo
07/07/1995
14/08/1996
27/01/1937
08/01/1999
08/01/2099
08/21/2099
08/01/2399
Padrão
Dia: [0-3]\d\/
Mês: [01]\d\/
Ano: [12][0129]\d{2}
Bom, podemos declarar algumas variáveis com o pattern em string e depois concatena-los, passando para o
construtor:
Sim, tivemos que escapar cada barra invertida que encontramos, mas agora o código (apesar de maior) ficou
claro e caso precisamos alterar o pattern futuramente, não ficaremos confusos e com frio!
Métodos
Finalmente vamos começar a ver os métodos dos objetos do tipo RegExp. Assim como a maioria dos tipos
de dados em Javascript, RegExp também possui vários métodos, mas vamos nos focar nos dois principais,
exec e test . E talvez o segundo seja ainda mais útil que o segundo do ponto de vista do dia-a-dia.
Exec
Esse é o método que você irá utilizar quando você precisa recuperar o dado que está filtrando. Ele é aquele
tipo de função em que precisamos iterar através de um while , pois, a cada vez que você executa o método,
ele pula para o próximo resultado.
Para provar isso, veja o exemplo abaixo:
Perceba que no último console, chamamos o método novamente e ele foi para o próximo resultado da regex.
Quando o resultado de pattern.exec for undefined, automaticamente o while receberá false e sairá do loop.
Estrutura do resultado
Quando executamos o método exec , temos um um array e em seguida, esse array é atualizado e
transformado em um object com algumas informações. São elas:
Assim, perceba que é dentro do laço while que você extrairá seus dados.
Test
O método test, como disse anteriormente, talvez seja muito mais útil que o exec no dia-a-dia. Mas, porque?
Bem, o test apenas valida se aquele alvo contém o padrão que você definiu, retornando um true ou false .
Aqui, utilizamos o padrão de quebrar em partes a regex. Ficou um pouco confuso porque como visto no
artigo sobre meta-caractere (LINK) e grupos (LINK), o () é avaliado como criação de um grupo, logo,
precisamos escapa-lo com uma barra invertida ( \ ). Porém como já visto, em strings precisamos escapar a
barra invertida também.
O resultado do primeiro console é expressão criada. O do segundo é o teste de verdadeiro ou falso para
saber se aquele alvo obedece a nossa expressão. Dessa maneira, podemos ter algo parecido com:
if(regexTelefone.test(alvo)){
//Faça alguma lógica/ação
}else{
throw new Error('Número de telefone fora da máscara')
}
Mais e mais usos de Regex
Além dos métodos da própria regex, há outros tipos de dados que aceitam expressões regulares como
parâmetro ou usamos regex manipular resultados. Abaixo alguns deles:
String.replace
String.split
String.match
Array.filter
Conclusão
Com isso, fechamos a saga das expressões regulares (mentira, ainda tem um bônus). Espero ter consigo
fazer você pelo menos sentir interesse de usar pequenas expressões no dia-a-dia, afinal, podemos usa-las em
diversos lugares que não foram citados, como por exemplo, o próprio terminal.