De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Julio Cezar Neves
Data de Publicação: 08 de abril de 2020
Esta dica é um pouco longa, mas vale cada minuto que você gastar lendo-a. Como diz o Julio ao final deste texto, programador que não conhece expressões regulares é um programador meia-boca. Conhecer bem expressões regulares é um pré-requisito para contratação em muitos lugares.
Em uma apresentação, sempre que falo em Expressões Regulares, fico atônito com a indiferença da plateia ao assunto. Só posso atribuir essa passividade ao desconhecimento, pois elas são extremamente úteis e as maiores aliadas de um programador, reduzindo drasticamente o tempo de desenvolvimento do código e de sua execução. Mas não para por aí, elas são sensacionais para definir regras de proxy e de firewall tornando as consultas bem mais rápidas., além de serem usadas por todas as linguagens de programação, também são usadas em todos os editores de texto.
Esse artigo é para incentivar o mergulho mais profundo daqueles que estão ali na beirinha d'água molhando o pé, isto é, daqueles que já deram uma pesquisada sobre o tema, mas que ainda não o estudaram a fundo para conhecerem a totalidade do seu potencial.
1. As Expressões Regulares sempre podem ser usadas quando não conhecemos um valor mas conhecemos as suas possíveis formações.
Exemplo
Descobrir todos os CEPs de um arquivo, usando ou não o hífen (-
) e o ponto
separador de milhares. Pense: como você programaria isso na sua linguagem
predileta? Para quem conhece Expressões Regulares, basta usá-las num
simples grep
.
$ grep -E '[0-9]{2}\.?[0-9]{3}-?[0-9]{3}' ARQUIVO
Pronto! Tá resolvido!
Vamos desmembrá-la:
[0-9]{2} |
Números ([0-9] ) que ocorrem duas vezes ({2} ); |
\.? |
Um ponto (. ) opcional (? ) a contrabarra (\ ) foi usado porque o ponto também é um metacaractere de Expressões Regulares, mas nesse caso, estava sendo usado como literal; |
[0-9]{3} |
Números ([0-9]) que ocorrem três vezes ({3} ); |
-? |
Um traço (- ) opcional (? ); |
[0-9]{3} |
Números ([0-9] ) que ocorrem três vezes ({3} ). |
2. Os parênteses formam grupos que permitem aplicar outros metacaracteres sobre o seu todo.
Exemplo
pegadas? |
Casa com pegada e com pegadas ; |
pega(das)? |
Casa com pega e pegadas porque o opcional (? ) foi aplicado a todo o grupo. |
Além do que acabamos de ver os parênteses (grupos) também retêm o texto que casou com a Expressão Regular do seu interior e portanto podemos usá-lo em outra parte da mesma Expressão Regular e é a isso que chamamos de retrovisores (tradução livre de back reference ou referência anterior).
Exemplo
(a)br\1c\1d\1br\1
casa com abracadabra
, mas:
(a)(br)\1c\1d\1\2\1
também casa.
Explicação
(a) |
Grupo que casou com o texto a ; |
br\1 |
Literal br seguido do 1º retrovisor (\1 ); |
c\1 |
Literal c seguido do 1º retrovisor (\1 ); |
d\1 |
Literal d seguido do 1º retrovisor (\1 ); |
br\1 |
Literal br seguido do 1º retrovisor (\1 ); |
Como o literal br
se repetia duas vezes, no 2º exemplo também criamos um
grupo com ele e por ser o 2º grupo criado, foi batizado como \2
.
Exemplo
$ sed -r 's/(.)/\1 /g' <<< abcdefgjij
a b c d e f g j i j
Observação: o comando acima é a forma mais rápida e mais limpa que:
$ echo abcdefgjij | sed -r 's/(.)/\1 /g'
O ponto é um coringa que casa com qualquer caractere e a flag g
no final
diz que a substituição é geral, assim sendo o ponto (.
) casou com cada uma
das letras que foi substituída pelo texto (a letra) casado (\1
) seguida de
um espaço em branco.
Olha esse mesmo exemplo (mal) escrito de outra forma:
$ sed -r 's/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/\1 \2 ... \10/' <<< abcdefghij
a b ... a0
Nesse caso salvamos cada uma das 10 letras em um retrovisor e vimos que ele
entendeu o \10
como um \1
seguido de um zero, isso porque só podemos usar até
9 retrovisores, mas lhes garanto que essa quantidade é mais que suficiente
para todas as tarefas.
Para transformar datas no formato DD/MM/AAAA em AAAAMMDD:
$ sed -r 's|([0-9]{2})/([0-9]{2})/([0-9]{4})|\3\2\1|' <<< 05/04/1947
19470405
Onde usei as barras verticais (|
) como os separadores do sed
para não
confundir com as barras (/
) da data. Desmembrando a Expressão Regular, vem:
([0-9]{2}) |
Número [0-9] de 2 algarismos {2} - casa com o dia |
/ |
O literal / entre o dia e o mês |
([0-9]{2}) |
Número [0-9] de 2 algarismos {2} - casa com o mês |
/ |
O literal / entre o mês e o ano |
([0-9]{4}) |
Número [0-9] de 4 algarismos {4} - casa com o ano |
Só mais um exemplo para podermos usar o grep
com Expressões Regulares.
A disposição do arquivo /etc/passwd
é a seguinte:
UserName:x:Uid:Gid:...
Pense na dificuldade que você teria para listar todos os usuário que tivessem o
Uid
idêntico ao Gid
. Com Expressões Regulares, fazemos isso em uma única linha:
$ grep -Eo '[[:alnum:]]+:x:([0-9]+):\1:' /etc/passwd
root:x:0:0:
daemon:x:1:1:
bin:x:2:2:
...
Desmembrando a Expressão Regular:
[[:alnum:]]+: |
Todas as alfanuméricas até encontrar o dois pontos (: ) |
x: |
O literal x : |
([0-9]+): |
Montando um grupo com algarismos (para casar com o Uid ) e terminando com dois pontos (: ) |
\1: |
Casará com um texto igual ao que foi salvo no Uid |
É interessante explicar que sempre usei o dois pontos (:
) como um
delimitador. Se não o fizesse, 123
casaria com 1234
. Veja:
$ grep -oE '(123):\1:' <<< 123:1234: &&
echo Casou ||
echo Não casou
Não casou
Tirando o dois pontos após o retrovisor:
$ grep -oE '(123):\1' <<< 123:1234: &&
echo Casou ||
echo Não casou
123:123
Casou
Usei a opção -o
(Only match) que mostra somente o texto casado,
para que você possa ver na segunda forma o casamento capenga que foi realizado.
A sintaxe que usei no início deste exemplo foi a mais simples de explicar, mas também poderia (e deveria) ter feito da seguinte forma:
$ grep -Eo '[[:alnum:]]+:x:([0-9]+:)\1' /etc/passwd
Embutindo o delimitador dois pontos (:
) no grupo, já que ele terá de
aparecer após os dois números.
A partir do Bash 4.0 o novo comando test
([[ ... ]])
passou a aceitar
Expressões Regulares, mas ele tem uma diferença interessante na forma
como salva os retrovisores. Ele usa o vetor (array) BASH_REMATCH
, que no seu
índice zero tem o casamento total feito pela Expressão Regular, no índice
um, o que seria o retrovisor \1
, e assim sucessivamente.
Exemplo
$ cat explica_test
#!/bin/bash
clear
read -p "Infome um endereço IP: " IP
[[ $IP =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]] ||
{
echo Isso não tem formato de endereço IP
exit 1
}
echo -e "
Casamento total:\t${BASH_REMATCH[0]}
Primeiro octeto:\t${BASH_REMATCH[1]}
Segundo octeto: \t${BASH_REMATCH[2]}
Terceiro octeto:\t${BASH_REMATCH[3]}
Quarto octeto: \t${BASH_REMATCH[4]}"
Algumas observações sobre esse cara:
=~
$ explica_test
Informe um endereço IP: 12.34.43.21
Casamento total: 12.34.43.21
Primeiro octeto: 12
Segundo octeto: 34
Terceiro octeto: 43
Quarto octeto: 21
$ explica_test
Informe um endereço IP: 12.34.43.2123
Isso não tem formato de endereço IP
A forma otimizada de fazer esta Expressão Regular seria:
[[ $IP =~ ^([0-9]{1,3}\.){3}([0-9]{1,3})$ ]]
Onde:
([0-9]{1,3}\.){3}
diz que o grupo formado por de 1
a 3
({1,3}
)
algarismos ([0-9]
) mais o ponto (\,
) ocorre 3 vezes ({3}
)
Mas nesse caso a demonstração estaria prejudicada, já que no primeiro retrovisor ficaria o último octeto casado por ele, ou seja o terceiro.
Apesar de ter abordado superficialmente as Expressões Regulares, já deu para dar uma boa introdução ao assunto.
Estou escrevendo esse artigo no LibreOffice e quando faço um <CTRL>+H
nele
e clico em Outras opções, veja o que aparece:
Se você é um cara que gosta de escrever código ou se vive às voltas com regras de firewall, aproveite o tempo que você tem disponível para estudar e, o mais importante praticar Expressões Regulares, pois aprender é muito fácil, mas para usá-las é necessário muita prática.
Existem na internet alguns emuladores de Expressões Regulares, neles a medida que você vai montando a expressão, ele vai mostrando paulatinamente o casamento. Não acho esse um bom meio de aprender, porque o software pensa por você e por isso não te motiva a aprender. Aconselho praticar Expressões Regulares usando sites (existem diversos) que você treina fazendo palavras cruzadas (crossword) cujas dicas são dadas por Expressões Regulares. Você aprende e se diverte.
Para finalizar vou te dar um choque de realidade: eu não contrato programadores que não conhecem Expressões Regulares, porque considero-os profissionais pouco produtivos, ou seja, quem não conhece Expressões Regulares é um programador meia boca ;)
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you are declaring that you agree with these rules:
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comentários (2)
Excelente texto! Grande Júlio, sempre didático e divertido. Acompanho esse mestre lendo seus livros, artigos e participando de eventos. Abraço, meu mestre e vida longa.
Valeu amigo, espero que eu consiga escrever outros de seu agrado, mas existem muitos outros aqui: https://www.dicas-l.com.br/autores/juliocezarneves.php.
Procuro sempre escrever tudo que sei, até esgotar o assunto.