Capítulo 6. Expressões regulares
Expressões regulares são uma maneira fácil de manipular texto de maneira concisa e rápida, e folgamos em dizer que elas são o recurso mais subutilizado de qualquer sistema que o suporte justamente porque a maioria das pessoas não se preocupa em aprendê-los. Expressões regulares têm um visual pouco convidativo, mas são simples de aprender e economizam muito tempo ao serem usadas. É possível economizar rotinas inteiras de shell script e loops complicados conhecendo apenas alguns poucos metacaracteres e construindo expressões regulares. Expressões regulares são suportadas por praticamente todos os programas Unix que suportam manipulação de texto, como vi, grep e sed. O próprio shell tem seus globs, que nada mais são que expressões regulares simplificadas. Linguagens de programação como awk, Perl e JavaScript trazem expressões regulares como parte da linguagem, outras linguagens como Python colocam a manipulação de expressões regulares na biblioteca padrão e ainda existem bibliotecas de manipulação de ERs para praticamente todas as linguagens de programação sérias, e também para algumas não sérias.
Isso dito, é necessário apenas um certo pensamento programático e conhecer os metacaracteres que fazem uma ER para poder usar esse recurso de forma eficiente. Um programador que não conhece ER pode, por exemplo, fazer um loop que busca várias variantes de uma palavra, ou ainda que analisa uma certa palavra caractere por caractere. O que ele está fazendo quando realiza esse tipo de operação é nada mais que reimplementando algo que já está implementado de forma menos genérica, mais lenta e mais trabalhosa.
A primeira parte do uso de expressões regulares é saber como encontrar texto. A partir disso, é possível realizar substituções simples, e com o uso de agrupamentos de partes da expressão, partir para substituições mais complicadas.
Vamos ver um exemplo simples: temos uma lista de pessoas, e no começo dessa lista queremos acrescentar o tratamento “Sr.”. Eis a lista:
A. Antônio J. da Silva V. Corleone M. Andrade H. Lekter
Sistematicamente, o que temos que fazer é acrescentar “Sr. ” ao começo de cada linha (com um espaço após o “Sr.”) para ter a lista que desejamos. Nenhuma substituição simples faria isso, é necessário usar metacaracteres das expressões regulares.
O primeiro metacaractere a ser usado é o ^, que significa “começo de linha- qualquer semelhança com o comando do vi para começo de linha não é coinscidência. Nossa expressão fica assim:
s/^/Sr. /
Para executar no vi, digite % e a expressão acima, lembrando que o % significa “em todo o arquivo”.
Isso quer dizer “substitua o começo da linha por ‘Sr. ‘ťť. O resultado é o desejado.
Outros exemplos típicos de ERs seriam “acrescente ‘Sra.’ na frente de nomes que terminam com ‘a’”, “coloque determinada string no fim da linha”, “substituia a ordem nome-sobrenome por sobrenome-nome”, etc. Para fazer esse tipo de tarefa, basta ter pensamento programático e conhecer os metacaracteres apresentados abaixo.
6.1. Metacaracteres
- . O metacaractere . casa com qualquer um caracter, seja pontuação, alfanumérico, espaço, tab, etc.
- * Encontra zero ou mais vezes o caractere anterior. Por exemplo, ba* casa com b, ba, baa, baaa, baaaa.
- [] Um grupo de caracteres. Por exemplo, mal[au] casa com “mala”e “malu”. Também é possível especificar “ranges”, como [a-f], que casa com qualquer letra entre a e f, e [a-fA-F], que casa também com maiúsculas.
- ^ Começo de linha. Adicionalmente, dentro de colchetes, ganha um significado completamente diferente: negação. Assim, [^a-f] significa “qualquer caractere exceto as letras de a até f.
- $ Fim de linha
- Escape. Se quiser casar o texto com um cifrão literal, use $ (caso contrário, você estará procurando um fim de linha.
Esse é o básico de expressões regulares, e isso é suportado em programas como o vi, o sed, o awk, o grep, perl e muitos outros. Alguns programas fazem uso de expressões regulares extendidas, como o egrep e o vi e o perl, que contém alguns metacaracteres úteis mas não essenciais.
6.2. Exemplos
AVISO: Como avisamos anteriormente, esta parte está incompleta!
6.3. sed e expressões regulares
O sed é um editor de texto de linha de comando baseado em expressões regulares muito útil para automatizar tarefas. A maneira básica de usá-lo é assim:
sed 'expressão-regular' arquivo
A saída do comando é enviada para a saída padrão. Como expressões regulares têm muitos caracteres que conflitam com os metacaracteres do shell, nós incluímos a expressão regular, por conservadorismo, sempre entre aspas simples.
Ao contrário do vi, o sed assume que todas as expressões devem ser realizadas em todas as linhas por padrão, então não é necessário especificar o caractere % antes da expressão.
6.4. Grupos
Uma operação muito comum é buscar algum texto desconhecido e depois realizar operações sobre esse texto. Vejamos o exemplo de uma lista de nomes:
Silva, José Asdrubal, Antônio Reis, Márcia
Como podemos usar uma expressão regular que inverte a ordem “Sobrenome, Nome”para “Nome Sobrenome”? Nós precisamos, em primeiro lugar, aprender a usar grupos.
Grupos são uma forma de preservar o texto casado para uso posterior em um comando de substituição. Por exemplo, nós precisamos criar uma ER que case com o sobrenome e coloque esse valor em um grupo e também case com o nome e coloque esse valor em outro grupo. Precisamos, em seguida, inverter a representação desses grupos para termos a ordem direta que precisamos.
A marcação desses grupos é feita colocando os trecos das ERs entre (). Porém, em uma ER, () têm seus próprios valores, então precisamos escapar os parênteses com uma barra invertida. Vejamos um exemplo de ER com marcação:
s/([a-z]*)[1-3]*/1/
Essa ER marca a primeira parte da expressão ([a-z]*]) em um grupo, casa com um número de 1 a 3 nenhuma ou mais vezes e depois troca isso tudo pela primeira parte apenas do grupo (na prática, eliminando os números). Note que o primeiro grupo marcado vira1, o segundo vira 2, etc.
Então como fazemos para inverter a ordem “Sobrenome, Nome”? Temos que casar o texto alfabética até a vírgula, sem incluir a vírgula, e botar em um grupo. Logo em seguida, devemos casar com qualquer texto alfabético e botar em um outro grupo. A ER fica assim:
s/([A-Za-z]*), ([A-Za-z]*)/2 1/
6.5. Classes de caracteres POSIX
Se você rodar a maioria dos exemplos acima com uma massa de dados maior, vai notar que as ERs falham quando encontram caracteres acentuadas. Isso acontece porque [a-z] [A-Z] casam apenas com caracteres não acentuados.
Para facilitar o gerenciamento de caracteres especiais, foram criadas classes de caracteres que contém uma série de caracteres de um tipo. Note, porém. que algumas características de caracteres são dependentes de fatores culturais. “á” é um caractere acentuado para um brasileiro, mas nem sequer existe para um inglês, então o funcionamento das classes de caracteres depende da configuração de localização do sistema operacional.
As classes de caracteres POSIX mais comuns são:
- [:alnum:] – Caracteres alfanuméricos
- [:alpha:] – Letras
- [:upper:] – Letras maiúsculas
- [:lower:] – Letras minúsculas
- [:digit:] – Números decimais
- [:space:] – Caracteres de espaçamento
- [:punct:] – Pontuação
O funcionamento de uma classe de caractere POSIX é o mesmo de um grupo como “a-z”. Logo, uma expressão regular que casa com uma letra maiúscula qualquer poderia ser:
[[:upper:]]
O classe POSIX foi incluída entre colchetes pelo mesmo motivo que uma sequência como “a-z”seria: os colchetes marcam um grupo. Note que a classe POSIX já inclui colchetes, mas isso não elimina a necessidade de usar colchetes para marcar as classes!
Note, porém, que o funcionamento de classes POSIX está sujeito às configurações de seu sistema operacional. Esse é um ponto que o administrador de sistemas deve ajudar você. Note que nem todo sistema Unix suporta as características de internacionalização do português, então o uso de classes POSIX deve ser feito com muito cuidado.
6.6. Palavras finais
Expressões regulares são uma maneira muito prática de validar e manipular texto, mas por seu visual estranho acabam por ser pouco aprendidas e pouco usadas. Mas a “linguagem” expressões regulares é prática e e fácil de aprender, mesmo que aparente ser algo estranho. A manipulação de texto usando expressões regulares economiza tempo e linhas de código ineficientes, e quando bem usadas, facilitam a legibilidade do programa.