Expressões regulares, o básico – parte 2

regex2_abertura

A primeira parte foi focada em dois conceitos bastante importantes: a quantificação (quantas vezes um padrão ocorre) e o agrupamento (uma relação do que pode ocorrer). Agora é a vez de tratar a possibilidade de não se saber exatamente o que ocorrerá e em qual quantidade.

Caracteres curingas

Este não é necessariamente um conceito novo. Ele é implementado em diversas sistemas operacionais para se facilitar a operação com mais de um arquivo ao mesmo tempo. A quantidade de caracteres de curinga disponível pode variar mas tanto em alguma variante de UNIX como no Windows — antes dele no DOS e antes deles o CP/M — estão disponíveis o ponto de interrogação — ? — e o asterisco — * — que, respectivamente, substituem um caractere ou um conjunto (in)definido¹ de caracteres no nome do arquivo.

E em REGEX há um recurso que opera de modo bastante parecido.

Um caractere qualquer

Por exemplo, para listar todos os arquivos começados por “tty” e seguido de um caractere qualquer no diretório dev usa-se:

$ ls -l /dev/tty?
/dev/tty0 /dev/tty1 /dev/tty2 /dev/tty3 /dev/tty4 /dev/tty5
/dev/tty6 /dev/tty7 /dev/tty8 /dev/tty9

Em REGEX o caractere que tem esta função é o ponto — . — e para fazer a mesma coisa:

ls /dev/ | egrep "tty.$"
tty0
tty1
tty2
tty3
tty4
tty5
tty6
tty7
tty8
tty9

A saída é um pouco diferente mas o conteúdo exibido é o mesmo nos dois exemplos. Aqui resolvi incluir um caractere novo, o cifrão — $ — ele serve para indicar o final da linha, ou seja, como existem arquivos neste diretório que também contém “tty”, um outro caractere qualquer e mais alguma coisa; estes também casarão com o padrão (experimente retirar o “$” para ver o que acontece).

Para facilitar os próximos exemplos:

$ TTY=$( cd /dev/ ; ls tty* )
$ echo ${TTY} | fold -w 71 -s
tty tty0 tty1 tty10 tty11 tty12 tty13 tty14 tty15 tty16 tty17 tty18 
tty19 tty2 tty20 tty21 tty22 tty23 tty24 tty25 tty26 tty27 tty28 tty29 
tty3 tty30 tty31 tty32 tty33 tty34 tty35 tty36 tty37 tty38 tty39 tty4 
tty40 tty41 tty42 tty43 tty44 tty45 tty46 tty47 tty48 tty49 tty5 tty50 
tty51 tty52 tty53 tty54 tty55 tty56 tty57 tty58 tty59 tty6 tty60 tty61 
tty62 tty63 tty7 tty8 tty9 ttyprintk ttyS0 ttyS1 ttyS10 ttyS11 ttyS12 
ttyS13 ttyS14 ttyS15 ttyS16 ttyS17 ttyS18 ttyS19 ttyS2 ttyS20 ttyS21 
ttyS22 ttyS23 ttyS24 ttyS25 ttyS26 ttyS27 ttyS28 ttyS29 ttyS3 ttyS30 
ttyS31 ttyS4 ttyS5 ttyS6 ttyS7 ttyS8 ttyS9

Voltando ao exemplo anterior, mas substituindo o “$” já está tudo junto agora:

$ echo ${TTY} | fold -w 71 -s | egrep "tty.\s"
tty tty0 tty1 tty10 tty11 tty12 tty13 tty14 tty15 tty16 tty17 tty18 
tty19 tty2 tty20 tty21 tty22 tty23 tty24 tty25 tty26 tty27 tty28 tty29 
tty3 tty30 tty31 tty32 tty33 tty34 tty35 tty36 tty37 tty38 tty39 tty4 
tty40 tty41 tty42 tty43 tty44 tty45 tty46 tty47 tty48 tty49 tty5 tty50 
tty51 tty52 tty53 tty54 tty55 tty56 tty57 tty58 tty59 tty6 tty60 tty61 
tty62 tty63 tty7 tty8 tty9 ttyprintk ttyS0 ttyS1 ttyS10 ttyS11 ttyS12

O “\s” — space — serve para indicar tanto o caractere de espaço (0x20) como o de tabulação (0x09).

Um caractere opcional

Mas como fazer quando há um caractere que não é possível precisar qual é mas que também pode não ocorre? Por exemplo, para listar tanto o tty1, tty2, … como também os ttyS1, ttyS2, … se poderia montar um grupo com as duas possibilidades — “tty(S.|.)\s” — mas um jeito mais simples seria este aqui:

$ echo ${TTY} | fold -w 71 -s | egrep "ttyS?.(\s|$)"
tty tty0 tty1 tty10 tty11 tty12 tty13 tty14 tty15 tty16 tty17 tty18 
tty19 tty2 tty20 tty21 tty22 tty23 tty24 tty25 tty26 tty27 tty28 tty29 
tty3 tty30 tty31 tty32 tty33 tty34 tty35 tty36 tty37 tty38 tty39 tty4 
tty40 tty41 tty42 tty43 tty44 tty45 tty46 tty47 tty48 tty49 tty5 tty50 
tty51 tty52 tty53 tty54 tty55 tty56 tty57 tty58 tty59 tty6 tty60 tty61 
tty62 tty63 tty7 tty8 tty9 ttyprintk ttyS0 ttyS1 ttyS10 ttyS11 ttyS12 
ttyS13 ttyS14 ttyS15 ttyS16 ttyS17 ttyS18 ttyS19 ttyS2 ttyS20 ttyS21 
ttyS22 ttyS23 ttyS24 ttyS25 ttyS26 ttyS27 ttyS28 ttyS29 ttyS3 ttyS30 
ttyS31 ttyS4 ttyS5 ttyS6 ttyS7 ttyS8 ttyS9

O ponto de interrogação — ? — assinala que o caractere anterior, no caso a letra «S», pode ou não ocorrer. Aliás grupos e listas (já explico) podem ser marcadas como opcionais, não é exclusivo para caracteres.

Por último o “(\s|$)” serve para indicar após o “tty”, talvez um “S” e um caractere qualquer haverá um espaço ou o fim da linha. E isto só precisa ser feito por conta do “ttyS9” que está no final.

Mas como fazer para indicar um caractere que pode ocorrer mas que não se faz ideia exata de qual é?

$ echo ${TTY} | fold -w 71 -s | egrep "tty.?8"
tty tty0 tty1 tty10 tty11 tty12 tty13 tty14 tty15 tty16 tty17 tty18 
tty19 tty2 tty20 tty21 tty22 tty23 tty24 tty25 tty26 tty27 tty28 tty29 
tty3 tty30 tty31 tty32 tty33 tty34 tty35 tty36 tty37 tty38 tty39 tty4 
tty40 tty41 tty42 tty43 tty44 tty45 tty46 tty47 tty48 tty49 tty5 tty50 
tty51 tty52 tty53 tty54 tty55 tty56 tty57 tty58 tty59 tty6 tty60 tty61 
tty62 tty63 tty7 tty8 tty9 ttyprintk ttyS0 ttyS1 ttyS10 ttyS11 ttyS12 
ttyS31 ttyS4 ttyS5 ttyS6 ttyS7 ttyS8 ttyS9

Simples combine o ponto (qualquer coisa) com o ponto de interrogação (é opcional) e pronto.

Mais de um caractere

Apenas relembrando que o sinal de soma — + — indica uma ou diversas ocorrências e que o ponto de interrogação — ? — indica uma ou nenhuma. Digamos que a ideia seja simplesmente, tal qual se faz em Shell, procurar o arquivo começado com “tty” e terminado por “tk”?

$ echo ${TTY} | fold -w 71 -s | egrep "tty.*tk"
tty62 tty63 tty7 tty8 tty9 ttyprintk ttyS0 ttyS1 ttyS10 ttyS11 ttyS12

O que deu errado? Simples, a busca em REGEX tende a retornar o resultado que melhor se aplica àquilo que foi solicitado. O sinal asterisco serve para indicar nenhuma ou diversas ocorrências (ele funciona como um super “?”) e, no exemplo acima, pode ser traduzido como algo que comece com “tty”, seguido de qualquer coisa (literalmente) e acabe em “tk”.

A dica aqui é ser mais seletivo ao criar o critério de busca:

$ echo ${TTY} | fold -w 71 -s | egrep "ttyp.*tk"
tty62 tty63 tty7 tty8 tty9 ttyprintk ttyS0 ttyS1 ttyS10 ttyS11 ttyS12

Na verdade o asterisco deve ser usado com certo cuidado.

Finalizando esta parte

E encerrando com uma pequena revisão, o caractere de ponto indica uma ocorrência de qualquer caractere:

$ echo "aranha ariranha arranha arranja " | egrep "arran.a"
aranha ariranha arranha arranja

O ponto de interrogação indica a não obrigatoriedade de um caractere:

$ echo "aranha ariranha arranha arranja " | egrep "arr?anha"
aranha ariranha arranha arranja

Enquanto que o asterisco indica que o caractere se repetirá nenhuma ou diversas vezes:

$ echo "aranha ariranha arranha arranja " | egrep "ar*anha"
aranha ariranha arranha arranja

Na próxima parte será a vez de acrescentar o conceito das listas e de começar a misturá-los um pouco também. 🙂

Até!

Anúncios

Um comentário sobre “Expressões regulares, o básico – parte 2

  1. Pingback: Expressões regulares, o básico – parte 3 | giovannireisnunes

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s