2. Expressões regulares
Expressões regulares
Introdução
O conceito de expressão regular é um dos conceitos teóricos mais importantes de Computação, e também é muito usado na prática, em diversos tipos de atividades, como:
- Encontrar um arquivo no seu computador cujo texto contenha um determinado padrão. Por exemplo, encontrar todos os arquivos em um determinado diretório que contenham um número de CPF ou CNPJ.
- Verificar informações fornecidas por usuários que devem obeceder a formatos específicos, como números de telefone, endereços de email, CPF, RG, CEP, etc;
- Verificar se um determinado trecho de código é sintaticamente correto, isso é, se obedece à sintaxe da linguagem de programação em que foi escrito;
- Efetuar substituições de texto em um ou mais arquivos, caso o conteúdo obedeca um determinado padrão. Por exemplo substuir todas as ocorrências de "pcs", "psi" e "pea" em um conjunto de arquivos por "PCS", "PSI" e "PEA", respectivamente;
- Extrair informações em páginas da Web. Por exemplo, capturar os preços de produtos em uma página de busca do Buscapé.
O termo "expressão regular" também é conhecido por regex ou regexp (contração do termo em Inglês, regular expression).
Você pode pensar em uma expressão regular como um "padrão" ou uma "forma" que é usada para generalizar e descrever um conjunto de strings (sequências de caracteres) sem precisar listar cada string individualmente.
Por exemplo, digamos que você queira encontrar todos os números de CPF em um texto. Como o número de strings que representam CPFs é muito grande para listar um por um, você pode usar uma expressão regular que se "encaixe" em qualquer número de CPF, como este:
<NUM><NUM><NUM><PONTO><NUM><NUM><NUM><PONTO><NUM><NUM><NUM><TRAÇO><NUM><NUM>
Onde:
- <NUM>
é qualquer dígito entre 0 e 9;
- <PONTO>
é o caractere de um ponto (.
);
- <TRAÇO>
é o caractere de hífen (-
);
Qualquer CPF válido e pontuado obedecerá ao padrão definido acima.
Outro exemplo: um padrão para encontrar números de telefone, sem considerar o DDD. Telefones móveis têm 9 dígitos, enquanto telefones fixos têm 8. Um possível padrão seria:
<NUM_OPCIONAL><NUMx4>-<NUMx4>
Onde:
- <NUM_OPCIONAL>
é qualquer dígito ou a string vazia;
- <NUMx4>
é uma sequência de 4 dígitos quaisquer;
- O traço (-
) representa ele mesmo.
Sintaxe
Como você pode ver, não basta conseguir definir o seu termo de busca como um padrão. Precisamos definir uma sintaxe (um formato) para esses padrões. Vamos usar <TRAÇO>
ou -
para definir um hífen? Vamos usar <NUM>
várias vezes ou poderemos escrever coisas como <NUMx10>
quando quisermos especificar sequências do mesmo tipo de caractere?
Quase todas as linguagens e ferramentas que suportam o uso de expressões regulares utilizam a mesma sintaxe e, para poder usar expressões regulares no seu dia-a-dia, você também precisa conhecê-la.
A tabela abaixo é uma referência resumida dessa sintaxe (veja outras referências, mais completas, na seção Recursos Adicionais).
Referência
Significado | Exemplo | Strings válidas | Strings inválidas | |
caractere comum | Qualquer caractere que não seja especial corresponde a ele mesmo. Os caracteres especiais são: {}[]()^$.|*+?\ e - , quando aparece dentro de colchetes ([] ). |
frio |
|
|
. |
Corresponde a qualquer caractere, com exceção do \n |
m.r
|
|
|
? |
O caractere ou grupo precedente é opcional (i.e., pode estar presente ou ausente). |
carro?
|
|
|
* |
Especifica que o caractere ou grupo que precede o `*` deve aparecer 0 ou mais vezes. |
ba*
|
|
|
+ |
Especifica que o caractere ou grupo que precede o `+` deve aparecer 1 ou mais vezes. |
ba+
|
|
|
{n} |
Especifica que o caractere ou grupo que precedente deve aparecer exatamente `n` vezes. |
ba{3}
|
|
|
| |
Especifica conjunção (`OR`). |
ba(nana|rraca)
|
|
|
[abc123] |
Colchetes são usados para definir opções. Um grupo entre colchetes corresponde a qualquer um (e apenas um) dos caracteres especificados entre os colchetes. |
[2345]
|
|
|
\d |
Corresponde a qualquer dígito. Equivalente a `[0-9]`. |
\d
|
|
|
\s |
Corresponde a espaços em branco (` `, `\t`, `\n`). |
a\s
|
|
|
^ |
Corresponde ao início da string, ou início de uma linha, caso o texto tenha múltiplas linhas. |
^v
|
|
|
$ |
Corresponde ao final da string, ou final de uma linha, caso o texto tenha múltiplas linhas. |
!$
|
|
|
() |
Parênteses são usados tanto para agrupamento quanto para captura. |
ba(na){2}
|
|
|
.
, ?
, (
, )
, $
), basta escapar estes caracteres com uma barra invertida, \
. Por exemplo: a expressão que horas são\?
reconheceria a string "Pode me dizer que horas são?", porque o ponto de interrogação está devidamente escapado.
Uso prático de expressões regulares
Muitas linguagens de programação e ferramentas possuem suporte à interpretação de expressões regulares. A maioria das linguagens de programação populares possui mecanismos nativos para a manipulação e uso de expressões regulares. Além disso, muitas ferramentas de linha de comando, como find
, grep
, sed
e awk
são comumente usadas com regexes.
O conceito de expressão regular é muito poderoso e é uma ferramenta essencial para resolver diversos problemas práticos. Por outro lado, para alguns problemas, a expressão regular correta pode ser bastante complexa e, sem a devida documentação, impossível de ser compreendida mais tarde—veja este exemplo da expressão regular oficial para reconhecer endereços de email em diversas linguagens.
Para encerrar, uma citação de Jamie Zawinski, co-fundador do Netscape e do Mozilla:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Exercícios
Escreva expressões regulares para reconhecer os padrões descritos abaixo. Leia o enunciado até o final antes de começar.
Número do exercício | Descrição | Exemplos válidos | Exemplos inválidos |
2.1 | Um número USP, sem traços, pontos ou qualquer sinal de pontuação entre os dígitos. |
|
|
2.2 | Número de telefone (fixo ou celular), com DDD entre parênteses, código do país precedido por "+" e espaços. Sem traços. |
|
|
2.3 | Linhas de um arquivo que começam com um número de até dois dígitos, seguido por parênteses e por uma frase qualquer. A linha deve terminar com um ponto de interrogação. |
|
|
2.4 |
Um nome próprio, com as seguintes restrições:
|
|
|
Observações
- Nenhum destes exercícios tem uma resposta única. Dado um determinado padrão, é comum que existam várias expressões regulares que o reconheçam. Por exemplo, uma sequência de dois dígitos pode ser definida pelas expressões
\d\d
,\d{2}
,[0-9]{2}
,[0123456789][0123456789]
, etc. - Você pode usar o site regex101 para testar e entender suas expressões regulares. Utilize os exemplos válidos e inválidos fornecidos no enunciado, e escreva a sua expressão lá, até ficar satisfeito com o resultado.
Entrega
Entregar um arquivo zip com nome lab2-atv4-XXXXXX.zip
contendo um único arquivo chamado regex.txt
.
O arquivo deve conter uma linha para cada exercício da tabela acima, começando com o número do exercício, seguido de parênteses ()
), 1 espaço e a expressão regular que você criou para resolver o problema.
Exemplo:
2.1) \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b
2.2) (19|20)\d\d
2.3) ^\s*#.*$
2.4) /\*.*?\*/