Expressões Regulares em Javascript: Libere o poder oculto na prática — Parte 1 (Introdução)

Felipe Monobe
7 min readJun 5, 2017

--

Esta é mais uma série de artigos que escreverei (desta vez vou lançar conforme ir terminando) para tratar de um uma relíquia dos antigos deuses da programação. Iremos falar de expressões regulares! Há muitas verdades e mitos em torno do assunto, então vamos com calma.

Algumas pessoas, quando confrontadas por um problema, pensam “Já sei, vou usar expressões regulares.” Agora elas têm dois problemas. — Jamie Zawinski, 1997.

Ao terminar essa série de mais ou menos 5 partes, espero que estejamos menos como descrito por Zawinski e mais como no quadrinho. Não teremos mais medo de expressões regulares e poderemos praticar os fundamentos para ficar cada vez melhor. É algo que vai requerer esforço e tempo.

Segue o conteúdo da série até então:

Contexto

Bem resumidamente, o conceito de expressões regulares (regular expressions, regex, regexp, etc) é datado de 1950 e se refere a conjuntos de regras descritas em expressões escritas com intuito de encontrar padrões (patterns) em textos (sintam-se livres para comentar definições mais bonitas que essa).

OK, e no que isso nos é útil, mais precisamente 😤!?

Pensemos em algo mais prático, visualizem o seguinte: alguém (que não é da área de tecnologia) chega para você com o próprio notebook pedindo ajuda. Essa pessoa precisa extrair os valores de NCM (considerem um valor numérico) de notas fiscais eletrônicas que estão salvas em arquivos de extensão XML. Antes de imaginarem naquela aplicação marota em Java, C#, Python, C++, Javascript, etc, lembrem que na máquina dela mal temos um editor de texto decente (o que dirá uma IDE?) e bom… é quase meia noite depois de um dia longo de trabalho e você só quer resolver isso para dormir de boas sabendo que é alguém um pouco mais caridoso.

Como fazer?? Detalhe: são vários (~15.000) XMLs em diretórios e subdiretórios diferentes e a estrutura deles não está uniforme (em alguns, o NCM está contido num nó pai X e noutros, Y). Então é hora de escolher:

  • Manda a pessoa se f#!@ ?
  • Deixa para outro dia?
  • Sai procurando uma ferramenta online?
  • Bate no peito e faz com expressões regulares 💪!
Para fins de ilustração imaginem algo assim, só que pior.

Bom, esse foi um caso atual e verídico que aconteceu comigo. A pessoa era meu irmão e resolver isso me reafirmou a utilidade das regexes. Nesse caso eu usei o GREP (Globally search a Regular Expression and Print), que é um utilitário de sistemas operacionais Unix (hello pessoal devops), mas também tem versões extra-oficiais no Windows. Ele lê o conteúdo de arquivos e busca padrões em grandes volumes, de uma forma prática e objetiva.

Casos mais simples e rotineiros do uso de expressões regulares são: filtros de output de comandos em terminais, validações de e-mail, CPF, etc em campos de formulários ou funções de busca e substituição que tem no teu editor de texto/IDE preferido. Compiladores de código também as usam bastante, mas aí já não dá pra dizer que é simples.

Prós e Contras

Agora que sabemos como pode ser útil, vamos ver alguns prós e contras:

✔️

  • Útil;
  • Objetivo;
  • Rápido;
  • Onipresente;
  • THUG LIFE 😎;

✖️

  • Alta curva de masterização;
  • Intercambiável (em alguns casos);
  • Falta de padronização;
  • Grandes poderes vem com grandes responsabilidades;

Falemos de dois contras importantes (vai ser rápido, eu juro)…

Regexes são fáceis de decorar a sintaxe. E para falar a verdade, você nem precisa. Com uma colinha (cheatsheet) ao lado, consegue escrever as expressões sem problemas. E muitas vezes é possível resolver um problema de várias formas diferentes (apesar de algumas soluções serem mais seguras, práticas, performáticas e chiques). Mas e o problema 🤔? Então… a dificuldade está em conseguir realmente ficar BOM nisso. E com isso quero dizer:

  • Enxergar soluções usando regexes para resolver cenários difíceis e improváveis, e não só aqueles de praxe (como os que já citei);
  • Aprender a identificar e mensurar precisamente o nível de restrição que cada expressão precisa ter, para então…;
  • Acertar as expressões de primeira! Ou quase. Eliminando a necessidade de “revisitá-las” várias vezes para dar aquela arrumadinha por causa de um match que deveria ou não dar;

Outro problema que vale ressaltar é que apesar de ser amplamente implementado em linguagens e ferramentas, as regexes têm pequenas diferenças entre uma implementação e outra, faltando um pouco mais de padronização. Felizmente, o conceito geral é comum em todas, mas algumas funcionalidades ou partes da sintaxe podem ter ou não dependendo de onde usar.

Chega de falatório, vamos começar a aprender, né?

Construindo a sua primeira expressão regular

Para os exemplos de código usarei expressões regulares no Javascript, acredito que seja uma linguagem bem presente no dia-a-dia da maioria dos devs e que por sua origem na web, está constantemente trabalhando com dados de texto.

Afinal, como construimos um objeto de expressão regular? se estiver com a versão do teu Javascript em dia, há dois modos simples:

Magia negra nível 1

O primeiro usa um construtor de assinatura RegExp(padrao[, flags]), onde o argumento padrao é a tua expressão, e flags é opcional, trataremos delas no futuro. Esse método compila em runtime. O segundo método é através de um RegExp literal, e é compilado no carregamento do script.

Apesar do segundo modo não precisar de um construtor explícito e parecer mais prático, ele não te permite passar uma variável para o padrão, impossibilitando a criação de regexes dinâmicas. Então não descarte o primeiro já de cara, algum dia vai precisar dele. Porém, opte pelo segundo caso o padrão seja estático, pois é mais performático.

Executando cada código e depois verificando o objeto criado, temos o mesmo resultado:

vanilla oldschool
cool bleeding-edge

E com isso criamos nossa primeira expressão regular!! Mas o quê? como? onde? quando? Bom, esse é o tipo mais direto e simples de expressão. É tão intuitiva que a maioria dos guias nem comenta sobre, e nem sei se tem um nome específico. Basicamente ele vai dar match num texto que contenha o termo “padraoDeBusca” contido em qualquer posição. Match? É Tinder? Não. Mas a analogia pode até servir. Um match é quando a tua expressão se verifica válida (encontra um correspondente) em algum trecho do texto analisado.

Vamos testar isso na prática? Apresento-lhes a função

.test(texto) do objeto RegExp. Ela recebe um argumento textodo tipo string e simplesmente confronta as regras contidas na expressão com ele, retornando true caso ocorra um match, e false caso negativo.

Vejam que espaços fazem diferença para nossa expressão.

Nossa.. mas essa expressão é totalmente inflexível (daria pra ser até mais). Como isso é útil? Eu preciso delimitar literalmente a palavra inteira? Calma, padawan! Essa é o nosso primeiro contato, ok? Com o que aprendeu até aqui já é possível fazer uma busca e/ou substituição simples de texto. Caso não saiba, há a função de objetos string .replace(regexp|substr, newSubStr|function[, flags]) que pode receber: duas strings de parâmetros (termo de busca e termo de substituição) mas o primeiro parâmetro string pode ser substituido por uma RegExp, apesar de até com o que aprendemos, seria praticamente a mesma coisa (intercambiável, lembra?).

Lembra daquele cenário que eu comentei lá em cima de usar o GREP para filtrar output de comandos de terminais? Essas simples expressões podem ser usadas justamente pra isso. Em sistemas Unix, por exemplo, as vezes precisamos filtrar listagens que são obtidas através de comandos tipo ls, lspci ou lsusb que listam: pastas e arquivos no diretório atual, componentes de hardware nas portas PCI e componentes de hardware nas portas USB, respectivamente. Os resultados sempre são enormes e é trabalhoso encontrar algo de forma eficiente. Geralmente os usuários mais experientes usam o operador pipe (|) para encadear o resultado de uma listagem com um segundo comando GREP, de expressão regular. Abaixo pode-se ver a diferença nos outputs finais gerados em ambos os casos, sem o filtro da expressão regular o conteúdo nem cabe na tela.

O comando filtrado por grep está highlighted. O outro está instaurando o caos 👿.

Glossário do dia

  • Regular expressions / Regex / RegExp: expressões para análise textual;
  • Pattern: o conjunto de regras (expressão);
  • GREP (Globally search a Regular Expression and Print): programa Unix que permite rodar simultaneamente regexes em arquivos e pastas;
  • Cheatsheet: listas resumidas de comandos (colinha);
  • Match: ocorrência (presença) de um pattern no texto analisado;
  • .test(texto): (RegExp) verifica se há matches no texto;
  • .replace(regexp|substr, newSubStr|function[, flags]): (string) substitui um match regexp ou termo substrpor um segundo termo newSubStr;

Revisando

Portanto, hoje falamos um pouco da definição do que é uma expressão regular, aprendemos um pouco da terminologia envolvida, vimos uma breve história, exemplos de código e problemas, motivos para usar e não usar regexes e construímos a nossa primeira expressão com Javascript e como já colocá-la em prática. Dessa vez tivemos muito texto e pouco código, mas nos próximos artigos isso vai inverter. Nos vemos na parte 2 (Metacharacters)!

Até logo, sras e srs!

/keepCoding 🤓/

--

--