Usando grupos em expressões regulares

grupos_regex-1_abertura

Na primeira parte da introdução às expressões regulares, falei dos grupos e que eles servem para agrupar valores distintos para uma pesquisa, ou seja, se você quiser pesquisar pelos nomes perl, python ou ruby em uma determinada sequência basta fazer algo como “(perl|python|ruby)” para tentar encontrá-los. Mas acontece que os grupos não servem só para isto.

Com eles é possível não só localizar mas também fazer referência ao que foi encontrado e assim ampliar as funcionalidades de algumas ferramentas. E aqui alguns exemplos bem simples…

Usando em pesquisas

Um uso interessante para os grupos é o de facilitar a busca em caso de repetições de uma determinada sequência. Como a ocorrência é “recordada”, ela pode ser utilizada outra vez dentro da própria expressão e assim simplificá-la. E para demonstrar, um exemplo utilizando quatro parágrafos do “bom e velho” texto do lorem ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris hendrerit ligula neque, v
itae tempus nulla scelerisque et. Nunc feugiat pellentesque dictum. Vivamus dignissim nis
l eget suscipit tempus. In hac habitasse platea dictumst. Quisque id gravida velit. Quisq
ue sed dictum leo. Sed aliquet sodales interdum. In justo tellus, imperdiet ut dignissim 
a, varius in sapien. Integer malesuada vestibulum leo, vel eleifend ex rhoncus sit amet. 
Mauris egestas rhoncus porttitor. Curabitur sed dolor ac tortor blandit mollis quis vel m
assa.
Ut sit amet faucibus neque. Vivamus maximus sodales ex sed imperdiet. Aliquam sed digniss
im nisl, id semper felis. In ac lacus quis velit mattis interdum. Vestibulum ante ipsum p
rimis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean pulvinar egestas 
mauris non facilisis. Quisque efficitur diam placerat, iaculis turpis a, semper felis. Ma
ecenas a dolor eget ex volutpat pretium. Suspendisse aliquet turpis quis auctor congue. D
onec tempus volutpat lorem. Praesent accumsan nec magna eget iaculis. Vestibulum commodo 
accumsan odio ut tincidunt. Phasellus ut tortor laoreet, facilisis turpis luctus, pulvina
r nisi. Etiam aliquam lorem et justo pretium posuere.
Sed malesuada, arcu id semper lacinia, odio leo ultrices magna, vitae condimentum nibh ur
na eget ligula. Aenean sed maximus augue, sed volutpat orci. Aliquam erat volutpat. Praes
ent consectetur eleifend tincidunt. Etiam non mauris feugiat, ullamcorper neque id, aucto
r dolor. Etiam in ex nunc. Praesent euismod eros a nibh volutpat elementum. Vestibulum vo
lutpat eget ex sed interdum. Nam congue enim mauris, ut placerat sapien fringilla vehicul
a. Fusce dictum odio sodales consequat pellentesque. Proin commodo velit placerat hendrer
it feugiat. Nam egestas venenatis mi, non efficitur sapien. Nam feugiat mauris arcu, nec 
efficitur sapien rhoncus eu.
Nulla aliquam ut tellus ac lobortis. Morbi lacus orci, placerat ac porttitor et, egestas
nec turpis. Suspendisse elit magna, feugiat id feugiat ac, tristique vel magna. Sed rutru
mneque at erat.

O objetivo aqui é pesquisar pela ocorrência da palavra lorem dentro dentro de uma sequência de linhas de texto mas no caso dela se repetir duas vezes (note que a palavra ocorre no 1º e 2º parágrafos mas em quantidades distintas).

A solução mais simples é replicá-la dentro da expressão regular, algo como “[Ll]orem.+[Ll]orem”, porém é possível colocar o que se deseja procurar em um grupo e referenciá-la novamente usando o “\1″¹ que significa: o primeiro grupo capturado dentro da expressão².

$ egrep --line-number "([Ll]orem).+\1" lorem_ipsum.txt
2:Ut sit amet faucibus neque. Vivamus maximus sodales ex sed imperdie
t. Aliquam sed dignissim nisl, id semper felis. In ac lacus quis veli
t mattis interdum. Vestibulum ante ipsum primis in faucibus orci luct
us et ultrices posuere cubilia Curae; Aenean pulvinar egestas mauris 
non facilisis. Quisque efficitur diam placerat, iaculis turpis a, sem
per felis. Maecenas a dolor eget ex volutpat pretium. Suspendisse ali
quet turpis quis auctor congue. Donec tempus volutpat lorem. Praesent
 accumsan nec magna eget iaculis. Vestibulum commodo accumsan odio ut
 tincidunt. Phasellus ut tortor laoreet, facilisis turpis luctus, pul
vinar nisi. Etiam aliquam lorem et justo pretium posuere.

Muito trabalho? Mas imagine se ao invés de lorem a palavra a ser buscada agora é dictum! Neste caso basta fazer uma simples mudança e pronto!

$ egrep --line-number "([Dd]ictum).+\1" lorem_ipsum.txt
1:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris hen
drerit ligula neque, vitae tempus nulla scelerisque et. Nunc feugiat 
pellentesque dictum. Vivamus dignissim nisl eget suscipit tempus. In 
hac habitasse platea dictumst. Quisque id gravida velit. Quisque sed 
dictum leo. Sed aliquet sodales interdum. In justo tellus, imperdiet 
ut dignissim a, varius in sapien. Integer malesuada vestibulum leo, v
el eleifend ex rhoncus sit amet. Mauris egestas rhoncus porttitor. Cu
rabitur sed dolor ac tortor blandit mollis quis vel massa.

E se o critério mudar novamente, agora para pesquisar ambas as ocorrências tando de lorem e dictum, basta relembrar como os grupos funcionam.

$ egrep --line-number "([Ll]orem|[Dd]ictum).+\1" lorem_ipsum.txt
1:Lorem ipsum dolor sit amet, consectetur adipiscing elit. (...)
pellentesque dictum. Vivamus (...) Quisque sed dictum leo. rabitur
sed dolor ac tortor blandit mollis quis vel massa.
2:Ut sit amet faucibus neque. Vivamus maximus sodales ex (...)
turpis quis auctor congue. Donec tempus volutpat lorem. Praesent
(...) Etiam aliquam lorem et justo pretium posuere.

E assim exibir ambos os resultados.

(¹) O termo utilizado pela documentação é backreference mas poderia ser traduzido como retro referência ou referência retroativa.

(²) Justamente, o segundo grupo será o “\2” e a coisa segue assim até o nono elemento, o “\9”.

Utilizando para substituir

Já que se pode marcar partes da pesquisa com um grupo é possível utilizá-las para para efetuar operações de transformação nas sequências pesquisada como, por exemplo, inverter a disposição como um nome próprio foi grafado…

$ echo "Giovanni dos Reis Nunes" |\
  sed "s/\([A-Z][A-Za-z ]\+\) \([A-Z][a-z]\+\)/\2, \1/"
Nunes, Giovanni dos Reis

Já que a sintaxe para expressões regulares dentro do sed tem algumas diferenças é exibi-las sem todos os caracteres de escapa para facilitar a leitura.

([A-Z][A-Za-z ]+) ([A-Z][a-z]+)

Esta expressão define dois grupos, um para nome, prenome e sobrenomes e um outro exclusivo para último sobrenome. E lembrando o exemplo do grep, o primeiro grupos será o “\1” enquanto que o segundo o “\2”.

Na segunda parte do comando, o texto que substituirá aquele que foi encontrado, usando apenas as referências aos grupos.

\2, \1

E o sed cuidará do resto.

Usando para extração

grupos_regex-1_lorem_ipsum

Voltando ao exemplo do lorem ipsum mas desta vez em uma versão formatada em HTML e com um objetivo diferente, extrair o conteúdo dos trechos marcados em negrito e em itálico e usando as funções do módulo de expressões regulares do Python, o re.

A primeira parte é carregar o módulo e também o arquivo de onde extrair os dados³.

»»» import re
»»» lorem_ipsum = open('lorem_ipsum.html').read()

Já que o conteúdo do arquivo foi carregado e armazenado na variável lorem_ipum, basta usar a função findall() com a expressão regular adequada para extrair tudo o que se deseja de uma vez.

»»» re.findall('«strong»(.+)«/strong»', lorem_ipsum)
['Mauris hendrerit ligula neque, vitæ tempus nulla scelerisque e
  t.', 'Donec tempus volutpat lorem', 'ullamcorper']

O resultado é uma lista contendo todas as ocorrências e como a definição do grupo não contém as tags o texto já vem “limpo”. Para fazer o mesmo com o texto em itálico, basta repetir o comando substituindo a expressão regular para “«em»(.+)«/em»”.

Mas que tal considerar que ao invés de ler um único arquivo HTML, serão lidos vários? Neste caso é mais prático (e rápido), criar também um objeto de expressão regular para armazenar esta pesquisa. Isto é feito através da função compile().

»»» italizado = re.compile('«em»(.+)«/em»')
»»» italizado
re.compile('«em»(.+)«/em»')

Assim a variável italizado armazenará uma versão pré compilada da expressão regular, daí basta chamar o método .findall() com o conteúdo a ser pesquisado como parâmetro.

»»» italizado.findall(lorem_ipsum)
['In justo tellus, imperdiet ut dignissim a, varius in sapien.',
 'faucibus orci luctus et ultrices posuere cubilia Curæ;',
 'Etiam non mauris feugiat, «strong»ullamcorper«/strong» neque id',
 'Morbi lacus orci, placerat ac porttitor et, egestas nec
 turpis.']

E pronto! Claro, aqui há um fragmento de HTML que justamente corresponde a um dos casos de negrito listados acima.

(³) Por conta do péssimo hábito do WordPress de tentar interpretar tags HTML, preferi substituir os sinais de maior e menor que por aspas francesas. 🙂

Finalizando…

Estes são exemplos bem simples, porém flexíveis o bastante para serem facilmente adaptados para resolução de diversas outras tarefas. E lembrando que os arquivos usados aqui estão disponíveis no repositório git deste blog lá no GitHub.

Até!

Anúncios