Вы находитесь на странице: 1из 68

Expresses Regulares com Delphi

Esta a sua cpia pessoal da apostila

Ol! com grande satisfao que lhe disponibilizamos esta apostila


sobre o tema Expresses Regulares com Delphi.

Por conta de um compromisso em poupar recursos e contribuir por um


mundo melhor no imprimiremos o material e acreditamos que em geral no
h necessidade de se fazer isto. Por isto esta uma cpia pessoal do material.

Esperamos que voc compartilhe este material com seus colegas.

Sugestes de melhoria sero sempre bem-vindas!

Muito obrigado pelo prestgio de sua companhia.

Sobre esta apostila

Verso: 001 - Abril/2014


Revisor: Jos Mrio Silva Guedes

Sobre a arrayOF

A arrayOF Consultoria e Treinamento tem por filosofia desenvolver o


potencial de seus parceiros ensinando e aprendendo com eles.

1 de 68
Expresses Regulares com Delphi

Sumrio
Esta a sua cpia pessoal da apostila ...................................................................... 1
Sobre esta apostila ........................................................................................................ 1
Sobre a arrayOF ............................................................................................................. 1
Material de apoio .......................................................................................................... 5
Introduo ...................................................................................................................... 5
Propsito da Expresso Regular................................................................................... 6
Princpios bsicos ........................................................................................................ 6
Motor RegEx ................................................................................................................ 7
Utilizao no Delphi ................................................................................................... 7
Dominando os meta-caracteres ................................................................................. 9
Ponto: . ....................................................................................................................... 10
Escape: \ ................................................................................................................... 11
Circunflexo: ^ ............................................................................................................ 12
Cifro: $...................................................................................................................... 13
Pipe: | ........................................................................................................................ 14
Colchetes: [ ] ............................................................................................................. 15
Quantificadores ........................................................................................................ 16
Estrela: * .................................................................................................................. 16
Cruz: + ..................................................................................................................... 18
Interrogao: ? ..................................................................................................... 18
Chaves: {n,n } ........................................................................................................ 19
Ganncia versus Preguia .................................................................................. 21
Quantificador possesivo ...................................................................................... 22
Parnteses: ( ) ........................................................................................................... 24
Agrupamento com alternncia: ........................................................................ 24
Agrupamento com quantificador: .................................................................... 25
Referncia Anterior: ............................................................................................. 26
Utilizao na linguagem hospedeira ................................................................ 27
Modificador de grupo: ............................................................................................ 29
Grupo de no captura: (?: subexpresso) ....................................................... 29
Grupo de comentrio: (?# comentrio) ......................................................... 30
Grupo nomeado: (?<nome> subexpresso) .................................................... 30

2 de 68
Expresses Regulares com Delphi

Grupo modificador de modo: (?i) e (?-i) .......................................................... 31


Modificador de RegEx................................................................................................. 33
Aplicativo de estudo ............................................................................................... 33
Os modificadores ..................................................................................................... 36
Nenhuma opo marcada: roNone ................................................................. 36
Ativar combinao insensvel caixa tipogrfica: roIgnoreCase .............. 36
Ativar modo de captura explcita: roExplicitCapture ..................................... 37
Habilitar comentrios de linha: roIgnorePatternSpace .................................. 38
Compilar a RegEx: roCompiled .......................................................................... 39
Tratamento de mltiplas linhas: roMultiLine ...................................................... 41
Tratamento de linha simples: roSingleLine ........................................................ 42
Tipos para Manipulao de ReGex no Delphi ........................................................ 44
Tipo: TRegEx ............................................................................................................... 45
Mtodo: TRegEx.IsMatch ..................................................................................... 46
Mtodo: TRegEx.Escape ...................................................................................... 46
Mtodo: TRegEx.Match ....................................................................................... 47
Mtodo: TRegEx.Matches.................................................................................... 47
Mtodo: TRegEx.Replace .................................................................................... 47
Mtodo: TRegEx.Split ............................................................................................ 50
Tipo: TMatch .............................................................................................................. 51
Mtodo: TMatch.NextMatch .............................................................................. 51
Mtodo: TMatch.Result ........................................................................................ 53
Tipo: TMatchCollection ............................................................................................ 54
Tipo: TGroup .............................................................................................................. 55
Tipo: TGroupCollection ............................................................................................ 55
Abordagens Avanadas com RegEx ....................................................................... 56
Grupo de condio: (?(<Retro referncia>)<ER verdadeiro>|<ER falso>) .... 56
Grupo olhar em volta (look around) ..................................................................... 57
Abreviaes de caracteres.................................................................................... 61
Abreviaes de classes de caracteres ................................................................ 61
Problemtica de acentuao e outras lnguas .................................................. 62
Classes de caracteres POSIX .............................................................................. 64
Trabalhando com Unicode ................................................................................. 65

3 de 68
Expresses Regulares com Delphi

Escape octal - \<nmero> ..................................................................................... 67


ncoras e outras asseres de largura zero ........................................................ 68

4 de 68
Expresses Regulares com Delphi

Material de apoio

Todos os cdigos dos exemplos citados nesta apostila esto disponveis


no GitHub:

https://github.com/arrayOF/expressoes_regulares_com_delphi.git

Introduo

Expresso Regular, que daqui para frente chamaremos apenas de


RegEx, uma das ferramentas mais teis que um programador pode contar. A
incorporao desta ferramenta a partir do Delphi XE nos oferece um salto de
qualidade nos projetos que no nos permite ignor-la.

No parece um assunto fcil. Mas ! O objetivo desta srie de artigos


o de desmistificar este assunto, em especial para ns programadores Delphi
que no tnhamos esta ferramenta to mo como temos hoje.

E perceba que voc levar este aprendizado, tal qual como voc ver
aqui, para outras linguagens com as quais eventualmente voc vier a
trabalhar, como Python ou JavaScript.

Antes, devemos ter em mente que o Delphi XE passou a incorporar uma


biblioteca de cdigo aberto denominada PCRE Perl Compatible Regular
Expressions (http://www.pcre.org/).

As units System.RegularExpressions, System.RegularExpressionsCore e


System.RegularExpressionsAPI encapsulam os OBJs (desenvolvidos em C++) e
por isso no necessitamos distribuir nenhuma DLL junto com o executvel. Essa
informao importante, pois caso voc encontre diferenas de uso em
outras linguagens por que essa outra linguagem provavelmente no seja
baseada na PCRE.

Outra informao importante se diz respeito aos usurios que utilizam


Delphi anteriores verso XE. Para estes existe uma opo em
http://www.regular-expressions.info/delphi.html sendo que a usei por muito
tempo e no h nada que a desabone.

5 de 68
Expresses Regulares com Delphi

Propsito da Expresso Regular

O objetivo primrio da RegEx a manipulao de texto. Mas de uma


forma tal que no h limites nem solues impossveis. E o melhor: De
implementao rpida e inequvoca.

Em geral a primeira dvida que surge em relao RegEx quanto sua


real utilidade. Afinal, sobrevivemos sem ela at hoje. Por que complicar? Mas
na verdade o que acontece que perdemos muito tempo com as outras
solues de manipulao de texto. E sempre fica um furo. Sempre.

Ento, a RegEx pode ser usada para:

Validao de entrada;
o e-Mail;
o Endereo IP;
o URL;
o Data;
o Telefone;
o CPF;
Anlise de LOG;
Consistncia de arquivo XML;
Busca e substituio de texto;
E qualquer outra soluo que envolva texto.

Princpios bsicos

Primeiramente, vamos conhecer os princpios bsicos. Temos que


considerar trs elementos ao se trabalhar com RegEx:

O padro a ser buscado: a RegEx propriamente dita, que no passa


de uma string com alguns smbolos especiais;
O texto a ser analisado: Basicamente uma string, com ou sem quebra
de linhas;
Os modificadores a serem aplicados: So sinalizaes que afetam o
comportamento da mquina RegEx;

O objetivo ao se aplicar uma RegEx a um texto justamente o de se


recuperar as combinaes. Podemos ter nenhuma, uma, ou vrias
combinaes. Cada combinao por sua vez pode ter vrios grupos. Se isso
no estiver fazendo sentido para voc, calma: far mais para frente.

J no tocante construo da RegEx, observe que ela constituda


por pequenos blocos de montagens, que se dividem em duas categorias:

6 de 68
Expresses Regulares com Delphi

Meta-caractere: So caracteres com funes especiais dentro


da RegEx;
Caractere literal: No tem funo especial dentro da RegEx,
sendo interpretado literalmente.

Cada parte de uma RegEx, em geral, referenciado por tomo.

No exemplo acima temos quatro caracteres. Deles, s o circunflexo


um meta-caractere enquanto que os outros so caracteres literais. E,
independentemente disso, cada caractere um tomo da RegEx.

Motor RegEx

Existe um elemento envolvido na RegEx que conhecido como motor


RegEx. Basicamente o algoritmo que resolve a Expresso Regular em um
determinado alvo. Obviamente no h espao para detalhar o
funcionamento do motor RegEx mas importante termos conscincia da
necessidade de estudarmos em algum momento suas peculiaridades para
podermos fazer uma RegEx mais assertiva.

Dentro do motor RegEx existe um conceito muito importante que o


backtracking. A grosso-modo, este conceito faz com que o motor RegEx
considere cada sub expresso ou tomo de cada vez e sempre que precisar
decidir entre duas alternativas viveis ele opta por uma e memoriza a outra
para voltar a ela mais tarde, se for necessrio.

Utilizao no Delphi

Todos os recursos necessrios para se trabalhar com RegEx est na unit


RegularExpressions.

A fim de testarmos gradualmente as expresses regulares no Delphi,


vamos desenvolver o seguinte aplicativo:

7 de 68
Expresses Regulares com Delphi

eRegEx:
TEdit

fRegEx:
mTexto:
TForm
TMemo
tvResultado:
clbModificador: TTreeView
TCheckListBox

bProcessar: TButton

Segue abaixo o cdigo do boto bProcessar.

O objetivo ser o de aplicar a RegEx descrita no Edit eRegEx no texto


descrito no Memo mTexto e colocar os resultados obtidos na TreeView
tvResultado.

A CheckListBox clbModificador ser usado mais para frente, em um


futuro artigo desta srie.

Este projeto ser incrementado ao longo dos artigos e o sua opinio


ser muito bem vinda.

procedure TfRegEx.bProcessarClick(Sender: TObject);


var
_RegEx : TRegEx; //Record responsvel por aplicar a expresso regular
_Matches : TMatchCollection; //Coleo de combinaes
_Matche : TMatch; //Uma combinao
oNode : TTreeNode; //N da treeview com as informaes de uma combinao
begin
tvResultado.Items.Clear;
_Matches := _RegEx.Matches(mTexto.Text,eRegEx.Text);
if (_Matches.Count > 0) then
begin
for _Matche in _Matches do
begin
oNode := tvResultado.Items.Add(nil,_Matche.Value);
tvResultado.Items.AddChild(oNode,Format('Index: %d',[_Matche.Index]));
tvResultado.Items.AddChild(oNode,Format('Length: %d',[_Matche.Length]));
end;
end;
end;

No decorrer desta apostila esse vai ser o mximo de cdigo que voc
ir digitar. isto mesmo! Voltaremos neste exato trecho para incrementar uma
coisa aqui e outra ali. Mas perceba que voc conseguir criar vrios cenrios
sem programar em Pascal.

8 de 68
Expresses Regulares com Delphi

Dominando os meta-caracteres

O domnio do assunto passa pelo pleno entendimento dos meta-


caracteres. Vamos conhecer a partir de agora a funo de cada um deles.
Mas perceba que isto s o comeo. A combinao entre eles multiplica as
possibilidades.

A figura abaixo mostra todos, isso mesmo, todos os meta-caracteres que


compes uma RegEx. So apenas 15 elementos.

Os meta-caracteres podem ser agrupados em quatro categorias:

Representante: Representa algum caractere, como o ponto que


combina com qualquer caractere naquela posio.
Quantificador: Determina a quantidade de ocorrncias de um tomo
da RegEx como o asterisco, que indica que nenhuma, uma ou mais
ocorrncias so permitidas do tomo predecessor.
ncora: Combina com uma posio no texto, a exemplo do cifro, que
combina com o fim de uma string (e no com o ltimo caractere).
Outros: Funcionalidades diversas.

Vamos aos nossos primeiros meta-caracteres.

9 de 68
Expresses Regulares com Delphi

Ponto: .

O meta-caractere ponto combina com qualquer caractere. Ele


representa uma classe de caracteres, assunto que veremos mais para frente.

Perceba que o ponto combinou com todos os caracteres da frase,


inclusive os espaos, gerando 18 combinaes.

10 de 68
Expresses Regulares com Delphi

Escape: \

A funo do escape, ou barra invertida, o de anular um meta-


caractere, tornando-o literal. O ponto, como vimos anteriormente, combina
com qualquer caractere. Mas e se quisermos combinar uma posio,
inequivocamente, com um ponto? Simplesmente escapa-se o ponto na
expresso regular.

Vale ressaltar que o escape, quando combinado com um no meta-


caractere, como o A maisculo, por exemplo, pode gerar uma meta-string,
oferecendo uma funcionalidade avanada. Conheceremos algumas meta-
strings em outro artigo.

11 de 68
Expresses Regulares com Delphi

Circunflexo: ^

O circunflexo do tipo ncora, ou seja, ele no combina com um


caractere especfico, mas sim com o incio de linha.

Observe no exemplo acima que temos trs ocorrncias da letra A, mas


conseguimos combinar somente com a do incio da linha usando o circunflexo
no incio da RegEx.

12 de 68
Expresses Regulares com Delphi

Cifro: $

A primeira coisa a considerar sobre este meta-caractere que o nome


dele cifro e no dlar como muitos se referem. Ele tambm do tipo
ncora e combina com o fim da linha.

Neste exemplo conseguimos capturar a ltima letra C, pois indicamos


que queremos uma letra C seguida pelo final da linha.

13 de 68
Expresses Regulares com Delphi

Pipe: |

O pipe nos oferece alternncia. O que nos d bastante versatilidade no


momento de se montar a RegEx.

Perceba no exemplo acima que obtivemos dois resultados com a


mesma RegEx: ou rato, ou roupa.

14 de 68
Expresses Regulares com Delphi

Colchetes: [ ]

Os colchetes representam uma classe de caracteres. Determina um


conjunto finito de possibilidades de combinao para aquela posio na
RegEx. Olhando de longe, parece com o pipe que vimos na seo anterior,
mas no tem nada a ver.

Perceba que criamos uma classe de caracteres com intervalo, pois


usamos o trao entre o 6 e o 9 dentro dos colchetes equivalendo a [6789].
Tambm poderamos negar esta classe e para isso bastaria usar o circunflexo
no incio da lista. Perceba na figura a seguir que conseguimos mudar
totalmente o resultado com a adio do circunflexo.

15 de 68
Expresses Regulares com Delphi

Quantificadores

Os meta-caracteres quantificadores determinam a quantidade de


ocorrncias do tomo anterior ao smbolo.

Existem trs tipos de quantificadores: gananciosos, preguiosos e


possesivos. Veremos mais sobre este detalhe mais para frente. No momento, o
que nos basta conhecer so os meta-caracteres quantificadores: * + ? { }

Estrela: *

A estrela comumente chamada de asterisco, porm o significado de


asterisco o de marcar notas sobre passagens de texto. Obviamente que
um mero detalhe de semntica, mas acredito ser uma definio importante.

A estrela indica que pode haver uma, vrias ou nenhuma ocorrncia do


tomo imediatamente anterior.

No exemplo acima, estamos procurando uma sequncia de caracteres


de zero a nove. Este exemplo timo, pois nos leva a um detalhe muito
importante: A estrela est se referindo ao tomo [0-9] e no apenas ao
colchete de fechamento da classe de caracteres, cujo assunto ns
abordamos na primeira parte.

Outro ponto sobre a definio da estrela sobre a possibilidade de no


haver nenhuma ocorrncia do tomo anterior. Isso significa que a RegEx no
ir falhar na ausncia do tomo anterior, como no exemplo abaixo:

16 de 68
Expresses Regulares com Delphi

17 de 68
Expresses Regulares com Delphi

Cruz: +

Normalmente nos referimos a este smbolo como adio ou


simplesmente mais. Mas o fato que o nome do smbolo cruz. A cruz indica
que deve haver uma ou mais ocorrncias do tomo anterior. Se a condio
no for satisfeita, toda a RegEx falha.

No exemplo acima, tivemos o mesmo resultado da estrela. Mas no


exemplo abaixo, percebemos que a RegEx falha.

Interrogao: ?

18 de 68
Expresses Regulares com Delphi

O meta-caractere interrogao exige apenas uma ou nenhuma


ocorrncia do tomo anterior. Perceba no exemplo abaixo que apenas o
ltimo zero entrou no resultado.

Vamos tirar os caracteres que formam a palavra reais para


observamos que ao invs de um nico resultado (1000), obtemos quatro
resultados distintos:

Chaves: {n,n }

19 de 68
Expresses Regulares com Delphi

As chaves criam um quantificador de intervalo, ou seja, indica um valor


mnimo mximo de ocorrncias do tomo anterior. importante notar que o
preenchimento dos valores de intervalo tem toda a relevncia.

No exemplo abaixo, estamos especificando que queremos de uma a


quatro ocorrncias do tomo [0-9].

Mas poderamos indicar um valor exato de ocorrncias: {4} ou somente


o valor mnimo de ocorrncias: {1,}

20 de 68
Expresses Regulares com Delphi

Ganncia versus Preguia

Os quantificadores, tais quais foram mostrados at aqui, se classificam


como quantificadores gananciosos. Ganancioso (ou guloso) se refere ao fato
do quantificador tentar casar com o mximo possvel, cedendo caractere
conforme a necessidade do prximo tomo.

No exemplo abaixo, temos uma linha CSV, necessidade muito comum


no dia a dia. O objetivo bvio de uma RegEx mnima o de recuperar os
campos individualmente. Mas veja o resultado da primeira tentativa:

Mas por que o motor RegEx no parou no primeiro ponto e vrgula no


texto, recuperando Campo 1;? Por que pelo fato da estrela ser gananciosa
ele percebeu o primeiro ponto e vrgula e guardou sua posio para voltar
atrs caso ele no encontrasse nenhum outro pela frente ou se o tomo
posterior exigisse isso.

Para resolver este problema lanamos mo, ento, dos quantificadores


preguiosos. Um quantificador preguioso sempre tenta parar a combinao,
somente combinando se o tomo seguinte falhar. Para tornar o quantificador
preguioso basta colocar uma interrogao aps o tomo:

Ganancioso Preguioso
* *?
+ +?
? ??
{n,n} {n,n}?

21 de 68
Expresses Regulares com Delphi

Por fim, eis a soluo para o problema inicial:

Solues envolvendo tags por exemplo (XML, HTML) passaro pela


deciso de se usar um quantificador ganancioso ou preguioso.

Quantificador possesivo

Existe ainda os quantificadores possesivos e so usados para otimizao


de RegEx. Usando o quantificador possesivo indicamos ao motor RegEx que
ela pode falhar imediatamente se o tomo no for satisfeito.

Para tornar um quantificador possesivo basta adicionar a cruz aps o


quantificador. Veja nossa tabela expandida.

Ganancioso Preguioso Possesivo


* *? *+
+ +? ++
? ?? ?+
{n,n} {n,n}? {n,n}+

Obviamente deve ser usado com muito cuidado, pois podemos


facilmente fazer com que nossa RegEx no obtenha o resultado desejado.
Veja no exemplo abaixo uma situao de falha.

22 de 68
Expresses Regulares com Delphi

Mas porque a RegEx falha? Porque o .* casou at o ltimo caractere.


Mas logo depois queremos um ponto vrgula, mas como usamos um
quantificador possesivo o motor RegEx no volta atrs e portanto falha. O fato
de o ltimo caractere ser justamente um ponto e vrgula meramente uma
coincidncia.

23 de 68
Expresses Regulares com Delphi

Parnteses: ( )

Os parnteses so utilizados para agrupar e capturar e logo


entenderemos do que se tratam esses termos. A utilizao bsica
exemplificada abaixo:

Figura 1: Exemplo bsico de utilizao de agrupadores

A princpio nenhuma novidade. Conseguiramos o mesmo resultado


sem o uso dos parnteses, ao menos neste cenrio. Vamos, ento, conhecer
os usos possveis.

Agrupamento com alternncia:

O agrupamento pode ser usado para limitar o escopo de uma


alternncia. Como sabemos, a alternncia oferecido pelo caractere pipe.
Digamos que em um texto qualquer queremos retornar tanto Delphi XE2
quanto Delphi 7.

A primeira alternativa talvez fosse Delphi XE2|Delphi 7 e isso


funcionaria. Mas seria inaceitvel em um RegEx maior e com muitas opes de
alternncia. Ento devemos limitar o escopo da alternncia com o
agrupamento:

Figura 2: Exemplo de agrupamento com alternncia

24 de 68
Expresses Regulares com Delphi

Esse recurso tambm melhora o desempenho da RegEx. Mas deve-se


tambm tomar cuidado com a ordem das alternativas, pois a primeira a
combinar a que vale mesmo que haja alguma alternativa mais exata
depois. Compare os resultados abaixo onde vou incluir o Delphi XE na busca:

Figura 3: Exemplo de agrupamento com alternncia com falha

No exemplo acima o Delphi XE2 no aparece na lista de resultados, pois


ao analisar a ltima linha do texto a RegEx retornou com sucesso ao satisfazer
segunda opo do grupo, no caso XE. Invertendo as opes dentro do
grupo temos o resultado esperado:

Figura 4: Exemplo de agrupamento com alternncia corrigido

Apesar disto, certamente a melhor RegEx para este cenrio seria:


Delphi (7|XE2?) onde o 2 seria opcional.

Agrupamento com quantificador:

O agrupamento tambm til para podermos quantificar as


ocorrncias do texto combinado. Vale as mesmas regras descritas no artigo
anterior sobre quantificadores gananciosos, preguiosos e possesivos. No

25 de 68
Expresses Regulares com Delphi

exemplo abaixo vamos listar todos os endereos IP de uma determinada


listagem:

Figura 5: Exemplo de agrupamento com quantificador

Perceba que o quantificador {4} (exatamente quatro ocorrncias) foi


aplicado ao grupo predecessor e no a um tomo especfico. Este grupo, por
sua vez, retorna um conjunto de um a trs caracteres numricos precedidos ou
no por ponto.

Referncia Anterior:

Um uso prtico do agrupamento na prpria RegEx, onde temos um


elemento chamado referncia anterior, tambm conhecido como retrovisor.
Como o nome sugere este elemento olha para trs. Ento, a funo de
captura justamente essa: oferecer para a RegEx o valor combinado pelo
parnteses para uso futuro.

O retrovisor representado pelas meta-strings de \1 at o \9 . Temos,


portanto, apenas nove possibilidades de reuso na mesma RegEx. Um bom
exemplo de uso em um formato XML, como no exemplo abaixo:

Figura 6: Exemplo de agrupamento com referncia anterior

Perceba que mesmo sem especificar o nome das tags de abertura e


fechamento conseguimos recuperar os elementos individualmente.

Vamos analisar a RegEx:

<([a-z]*)>.*</\1>

26 de 68
Expresses Regulares com Delphi

Interpretao tomo Resultado


Sinal de menor, seguido por: < <
Incio de grupo (
Qualquer caractere de a [a-z]* nome
z em qualquer quantidade
Fim de grupo, seguido por: )
Sinal de maior, seguido por: > >
Qualquer caractere em qualquer .* Jlio
quantidade, seguido por:
Sinal de menor, seguido por: < <
Barra normal, seguido por: / /
Valor do grupo 1, seguido por: \1 nome
Sinal de maior > >
Tabela 1: Anlise de expresso regular

Importante salientar que o retrovisor faz referncia ao resultado obtido


pelo grupo e no subexpresso contida nela.

Vale a pena ainda ressaltar que a contagem dos grupos se d na


mesma ordem das aberturas dos parnteses da esquerda para a direita,
mesmo que estejam aninhados.

Utilizao na linguagem hospedeira

No Delphi, assim como em outras linguagens que oferecem RegEx,


podemos tirar proveito do grupo. No exemplo anterior ns conseguimos isolar o
elemento XML: <nome>Jlio</nome>

Mas provavelmente estamos mais interessados em recuperar o


contedo do elemento e no no elemento como um todo. Isto possvel com
o tipo TGroup. Cada combinao, ou cada TMatch, pode ter um ou mais
grupos. Vamos, ento, modificar o cdigo do boto bProcessar conforme a
listagem abaixo:

procedure TfRegEx.bProcessarClick(Sender: TObject);


var
_RegEx : TRegEx; //Record responsvel por aplicar a expresso
regular
_Matches : TMatchCollection; //Coleo de combinaes
_Matche : TMatch; //Uma combinao
oNode : TTreeNode; //N da treeview com as informaes de uma
combinao
oNodeGrupo : TTreeNode; //N da treeview criando o n de grupos
oNodeItemGrupo : TTreeNode; //N do treeviwe criando um grupo
_Group : TGroup; //Um grupo
begin
tvResultado.Items.Clear;
_Matches := _RegEx.Matches(mTexto.Text,eRegEx.Text);
if (_Matches.Count > 0) then
begin
for _Matche in _Matches do

27 de 68
Expresses Regulares com Delphi

begin
oNode := tvResultado.Items.Add(nil,_Matche.Value);
tvResultado.Items.AddChild(oNode,Format('Index:
%d',[_Matche.Index]));
tvResultado.Items.AddChild(oNode,Format('Length:
%d',[_Matche.Length]));

{$REGION 'Analisando os grupos'}


if (_Matche.Groups.Count > 0) then
begin
oNodeGrupo :=
tvResultado.Items.AddChild(oNode,'Agrupamentos');
for _Group in _Matche.Groups do
begin
oNodeItemGrupo:=
tvResultado.Items.AddChild(oNodeGrupo,_Group.Value);
tvResultado.Items.AddChild(oNodeItemGrupo,Format('Index:
%d',[_Group.Index]));
tvResultado.Items.AddChild(oNodeItemGrupo,Format('Length:
%d',[_Group.Length]));
end;
end;
{$ENDREGION}

end;
end;
end;

Listagem 1: Novo cdigo do boto bProcessar

No exemplo a seguir o objetivo ser o de retornar apenas o contedo


do elemento XML nome:

Figura 7: Exemplo de grupo com captura

Perceba, porm, que retornou dois grupos: um com todo o elemento


XML e outro somente com o contedo. Isso acontece porque
independentemente de usarmos ou no um agrupamento na RegEx sempre
nos oferecido o grupo 0 (zero) que contm toda a combinao retornada.
Depois, se houver, retorna-se os agrupamentos definidos na RegEx.
Apesar de na RegEx podemos ter apenas nove referncias anteriores,
isso no se aplica no Delphi, sendo que ele retorna tantos grupos quanto
houverem, como no exemplo abaixo:

28 de 68
Expresses Regulares com Delphi

Figura 8: Exemplo de grupos com captura

Observe ainda que no cdigo do boto bProcessar fizemos um loop


com for..in..do listando todos os grupos retornados. Caso seja necessrio
capturar um grupo especifico pelo ndice basta us-lo em TGroupCollection
:

procedure TfRegEx.GrupoEspecifico;
var
_RegEx : TRegEx;
begin

ShowMessage(_RegEx.Match(mTexto.Text,eRegEx.Text,[]).Groups[11].Value)
;
end;

Listagem 2: Exemplo de retorno do valor de um grupo pelo ndice numrico

No exemplo acima estamos retornando para o ShowMessage a 11


captura retornada na combinao .

Modificador de grupo:

No bastasse o que vimos at aqui, existe um elemento chamado


Modificador de Grupo. Ele representado pela interrogao logo aps a
abertura do grupo. Em seguida deve vir o modificador propriamente dito.
Vamos conhecer alguns:

Grupo de no captura: (?: subexpresso)

Dependendo do tamanho da sua RegEx ou at mesmo do seu texto


alvo, e no sendo necessrio a referncia anterior, podemos poupar o motor
da RegEx desta tarefa. Isto tem influncia no desempenho da RegEx como um
todo. Se o grupo for simplesmente para limitar o escopo da alternncia ou
para determinar o quantificador, vale a pena abrir mo da captura.

29 de 68
Expresses Regulares com Delphi

Figura 9: Exemplo de grupo de no captura

Perceba no exemplo acima que no temos o grupo 1, temos somente o


grupo 0 que na verdade reflete o mesmo resultado da combinao. Portanto
no podemos nem usar o \1 na RegEx nem retornar o Groups[1] no Delphi.

Grupo de comentrio: (?# comentrio)

Dependendo da complexidade da RegEx talvez valha a pena


comentar alguns trechos. Pois bem, isso tambm possvel:

Figura 10: Exemplo de grupo de comentrio

Obviamente que o comentrio no influenciou no resultado.

Grupo nomeado: (?<nome> subexpresso)

A fim de contribuir para a legibilidade da RegEx bem como contornar o


limite de 9 referncias anteriores podemos nomear um grupo e fazer referncia
a este grupo tanto na RegEx quanto no Delphi:

Figura 11: Exemplo de grupo nomeado

30 de 68
Expresses Regulares com Delphi

No exemplo acima estamos nomeando o grupo para DDD. Para


recuperar no Delphi este grupo, usaremos um cdigo parecido com o que
segue:

procedure TfRegEx.GrupoEspecifico;
var
_RegEx : TRegEx;
begin
ShowMessage(_RegEx.Match(mTexto.Text,eRegEx.Text,[]).Groups['DDD'].
Value);
end;
Listagem 3: Exemplo de retorno do valor de um grupo pelo ndice nomeado

Ou seja, a mesma construo da listagem 2, mas ao invs de nos


referir ao ndice numrico, estamos nos referindo ao ndice nomeado.

E para fazer uma referncia anterior na RegEx usamos a meta strings


\k<nome> , como no exemplo abaixo, onde queremos apenas nomes
repetidos:

Figura 12: Exemplo de referncia anterior a um grupo nomeado

Mais uma vez, estamos fazendo referncia ao ndice nomeado ao invs


do ndice numrico, porm na prpria RegEx.

Grupo modificador de modo: (?i) e (?-i)

A princpio a RegEx case-sensitive. Digo a princpio, pois uma


caracterstica que podemos alterar. E uma forma de alterar na prpria
RegEx:

Figura 13: Exemplo de grupo modificador de modo

31 de 68
Expresses Regulares com Delphi

No exemplo acima estamos procurando a palavra Delphi


independentemente do modo em que esteja maiscula ou minscula.
Portanto o (?i) ativa o case-insensitive ao passo que o (?-i) desativa.

32 de 68
Expresses Regulares com Delphi

Modificador de RegEx

Os modificadores so muito importantes, pois modificam, ou seja,


influem no comportamento da mquina RegEx.

Aplicativo de estudo

Para estudarmos este elemento, vamos modificar o nosso programa de


exemplo que foi desenvolvido no primeiro artigo. Perceba que o TEdit que
colocamos para a escrever a RegEx ser trocado por um TMemo, passando a
se chamar mRegEx. Essa uma mudana importante e o motivo dela ser
explicado mais para frente. Por ora, tome cuidado ao digitar a sua RegEx
certificando-se que ela possui apenas uma linha.

Ento, proponha-se um novo layout:

mRegEx:
TMemo tvResultad
o:
TTreeView
mTexto:
TMemo

bProcessar: clbModificad
TButton or:
TCheckListBox

Figura 1: Lay-out do aplicativo de teste

Segue o print da janela Structure do Delphi dando uma ideia do


aninhamento dos elementos de tela.

33 de 68
Expresses Regulares com Delphi

Figura 2: Estrutura dos elementos de tela

Vamos aos cdigos.

A primeira coisa que precisamos saber que os modificadores so


representados pelo tipo enumerado TRegExOption e ao fazer as combinaes
devemos passar um conjunto de modificadores usando o tipo TRegExOptions.
Ento, para usarmos cada um deles em nosso projeto, vamos listar as opes
no CheckListBox clbModificador. Segue o cdigo do OnCreate do formulrio:

procedure TfRegEx.FormCreate(Sender: TObject);


var
eModificador : RegularExpressions.TRegExOption;
sModificador : string;
begin
//Varremos toda a coleo de modificadores
for eModificador := Low(TRegExOption) to High(TRegExOption) do
begin
//Transformamos o tipo enumerado para string
sModificador := TypInfo.GetEnumName(TypeInfo(TRegExOption),Ord(eModificador));
//Adicionamos a opo ao CheckListBox
Self.clbModificador.AddItem(sModificador,nil);
end;
end;
Listagem 1: Evento OnCreate do formulrio

Na listagem 2, vemos o cdigo do boto bProcessar. No houve


nenhuma modificao no que se refere ao uso da RegEx. S estamos
colocando uma informao adicional, que o tempo de processamento
para fazermos algumas consideraes sobre desempenho.

34 de 68
Expresses Regulares com Delphi

procedure TfRegEx.bProcessarClick(Sender: TObject);


var
_Matches : RegularExpressions.TMatchCollection; //Coleo de combinaes
_Matche : RegularExpressions.TMatch; //Uma combinao
_Group : RegularExpressions.TGroup; //Um grupo

oNode : TTreeNode; //N da treeview com as informaes de uma combinao


oNodeGrupo : TTreeNode; //N da treeview criando o n de grupos
oNodeItemGrupo : TTreeNode; //N do treeviwe criando um grupo

_cronus : Diagnostics.TStopwatch; //Cronmetro para medio do tempo


begin
Screen.Cursor := crHourGlass;
try
tvResultado.Items.Clear;

{$REGION 'Fazendo a pesquisa'}


_cronus := TStopwatch.StartNew;
_Matches := TRegEx.Matches(mTexto.Text,mRegEx.Text,Self.ObterModificadores);
_cronus.Stop;
{$ENDREGION}

{$REGION 'Registrando o tempo de processamento'}


if (_cronus.ElapsedMilliseconds = 0) then
begin
StatusBar1.Panels[1].Text := Format('Tempo de processamento: [%d]
Ticks',[_cronus.ElapsedTicks]);
end else
begin
StatusBar1.Panels[1].Text := Format('Tempo de processamento: [%d] Milisegundos - %d
Ticks',[_cronus.ElapsedMilliseconds,_cronus.ElapsedTicks]);
end;
{$ENDREGION}

{$REGION 'Analisando as combinaes'}


if (_Matches.Count > 0) then
begin
for _Matche in _Matches do
begin
oNode := tvResultado.Items.Add(nil,_Matche.Value);
tvResultado.Items.AddChild(oNode,Format('Index: %d',[_Matche.Index]));
tvResultado.Items.AddChild(oNode,Format('Length: %d',[_Matche.Length]));

{$REGION 'Analisando os grupos'}


if (_Matche.Groups.Count > 0) then
begin
oNodeGrupo := tvResultado.Items.AddChild(oNode,'Agrupamentos');
for _Group in _Matche.Groups do
begin
oNodeItemGrupo:= tvResultado.Items.AddChild(oNodeGrupo,_Group.Value);
tvResultado.Items.AddChild(oNodeItemGrupo,Format('Index: %d',[_Group.Index]));
tvResultado.Items.AddChild(oNodeItemGrupo,Format('Length: %d',[_Group.Length]));
end;
end;
{$ENDREGION}

end;
end;
{$ENDREGION}

finally
Screen.Cursor := crDefault;
end;
end;
Listagem 2: Evento OnClick do boto bProcessar

Perceba que no momento de processar a RegEx, o terceiro parmetro


est chamando o mtodo ObterModificadores do formulrio. Como dito
anteriormente, este parmetro requer um conjunto de opes o que

35 de 68
Expresses Regulares com Delphi

normalmente seria passado com colchetes. Para o nosso aplicativo ficar


funcional, vamos usar as opes selecionadas no CheckListBox. O cdigo
deste mtodo est na listagem 3:

function TfRegEx.ObterModificadores: TRegExOptions;


var
sModificador : string;
eModificador : RegularExpressions.TRegExOption;
i : Integer;
begin
//Iniciamos o Result como um conjunto vazio
Result := [];
//Varremos todos os itens do CheckListBox
for i := 0 to Pred(Self.clbModificador.Count) do
begin
//Verificamos se a opo est marcada
if (Self.clbModificador.Checked[i]) then
begin
//Gravamos a opo em um string auxiliar
sModificador := Self.clbModificador.Items[i];
//Transformamos a string para o tipo enumerado
eModificador := TRegExOption(TypInfo.GetEnumValue(TypeInfo(TRegExOption),sModificador));
//Inclumos o resultado no conjunto
Include(Result,eModificador);
end;
end;
end;
Listagem 3: Mtodo ObterModificadores

Os modificadores

Com o aplicativo desenvolvido e funcional, vamos entender, enfim,


cada um dos modificadores. So sete os modificadores, que podem ser
usados em conjunto ou isoladamente.

Nenhuma opo marcada: roNone

Esta opo indica que nenhuma opo est marcada. Porm, us-la
em conjunto com outras opes no as anula. Portanto, no se percebe um
uso prtico para este modificador, pois equivale a passar o conjunto vazio.

Ativar combinao insensvel caixa tipogrfica: roIgnoreCase

No artigo anterior conhecemos o grupo modificador de modo


representado pelas expresses: (?i) e (?-i)

O que estiver entre esses dois tomos ser combinado com maisculo
ou minsculo. Mas se esta regra se aplicar a toda a RegEx voc pode usar o
modificador roIgnoreCase.

36 de 68
Expresses Regulares com Delphi

Figura 3: Exemplo do roIgnoreCase

Ativar modo de captura explcita: roExplicitCapture

Ainda no artigo anterior vimos que os agrupadores tm as funes de


agrupar e capturar. A captura um recurso caro e o seu uso s tem sentido
com o uso de referncias anteriores, ou se usarmos o resultado na linguagem
hospedeira. Para usar um grupo sem a funo de captura podemos usar a
expresso: (?: subexpresso) .

Porm, dependendo do tamanho da sua RegEx, esta notao a poluir


visualmente tornando-a mais confusa. Como opo, ento, temos o
modificador roExplicitCapture que desliga a funo de captura de todos os
grupos, a no ser que ele seja um grupo nomeado, cuja expresso : (?<nome>
subexpresso).

No exemplo abaixo temos uma lista de pssaros e por algum motivo


queremos apenas os que tem nome composto repetido e que seja
classificado como feio ou bonito.

37 de 68
Expresses Regulares com Delphi

Figura 4: Exemplo do roExplicitCapture

A RegEx utilizada foi: (?<passaro>\w*)-\k<passaro>[ ][


](bonito|feio)

Perceba que usamos dois grupos, um para pegar o nome do pssaro e


outro para oferecer uma alternncia.

1 Grupo (?<passaro>\w*) Grupo de captura nomeado


2 Grupo (bonito|feio) Grupo de alternncia

Por fim, para tirar proveito do valor capturado pelo primeiro grupo
usamos o tomo:

\k<passaro> Este uma referncia anterior a um grupo nomeado

Perceba ainda na TreeView que o valor retornado pelo segundo grupo


no consta no resultado, situao que muda quando desmarcamos a opo
roExplicitCapture.

Habilitar comentrios de linha: roIgnorePatternSpace

Algumas RegEx podem realmente ficar complexas. Provavelmente


comentrios certos nos lugares certos ajudaro bastante a entender o objetivo
de uma RegEx sem precisar analisar tomo a tomo. Isso possvel com o
modificador roIgnorePatternSpace.

38 de 68
Expresses Regulares com Delphi

Esse modificador faz com que o motor RegEx ignore os caracteres de


espao na RegEx, exceto os que estiverem dentro das classes de caracteres.
Portanto, deve-se tomar um cuidado especial se o caractere de espao fizer
parte da sua RegEx, envolvendo-a em uma classe de caracteres.

Com isso, voc pode quebrar a sua RegEx em linhas e ao final de cada
linha iniciar um comentrio com o smbolo de tralha (#).

Na figura 5 um exemplo simples de anlise de um retorno de um


comando ping. A RegEx sem os comentrios seria: tempo(.*)[ ](.*)

Figura 5: Exemplo do roIgnorePatternSpace

Compilar a RegEx: roCompiled

Em vrios momentos eu cito o motor RegEx, que o algoritmo


responsvel pelo processamento da RegEx. Basicamente ela analisa a RegEx e
varre o texto alvo tomando as melhores decises possveis. Dependendo do
cenrio o desempenho pode no ser satisfatrio.

Uma forma de melhorar isso compilando a RegEx, onde criado um


mapa prvio com todos os caminhos possveis que a RegEx poder tomar. Isso

39 de 68
Expresses Regulares com Delphi

torna a inicializao mais lenta, porm torna as pesquisas, em especial as


repetitivas, mais rpidas.

40 de 68
Expresses Regulares com Delphi

Tratamento de mltiplas linhas: roMultiLine

Primeiramente temos que ter conscincia de que o texto alvo


meramente uma string que pode ou no ter quebra de linhas lgicas. Isso
um problema relevante se tratarmos arquivos gerados no Linux por exemplo.

No nosso dia a dia Delphi com Windows, o comum tratarmos textos


com o EOL (end of line) representados por CR+LF, que no Delphi corresponde
a #13#10.

Como visto no primeiro artigo, temos dois meta-caracteres ncoras que


representam o incio de linha e o fim de linha, respectivamente representados
por ^ e $ . O modificador roMultiLine justamente influi no comportamento
destes meta-caracteres.

Para exemplificar isto, vamos considerar uma lista de telefone, onde


queremos o DDD que so os dois primeiros dgitos de cada linha.

Figura 6: Exemplo de busca em mltiplas linhas sem o roMultiLine

Perceba na figura 6, que sem o roMultiLine retornou-se apenas um


resultado enquanto que com o roMultiLine vieram todos os resultados
desejados:

41 de 68
Expresses Regulares com Delphi

Figura 7: Exemplo de busca em mltiplas linhas com o roMultiLine

O mesmo comportamento se aplica ao meta-caractere $ .

Tratamento de linha simples: roSingleLine

Em algumas ocasies voc precisar tratar textos com quebras de


linhas como se fosse uma s. Um bom exemplo a anlise de um protocolo de
comunicao como, por exemplo, o HTTP. Para isso usamos o modificador
roSingleLine que influi no significado do meta-caractere ponto: .

A princpio o meta-caractere ponto combina com qualquer caractere.


Mas se tentarmos analisar um texto com quebra de linhas no seremos bem
sucedidos, a no ser que usemos o roSingleLine.

Na figura 8 estamos analisando um cabealho HTTP com o objetivo de


capturar o cdigo de retorno bem como o tamanho do contedo.

42 de 68
Expresses Regulares com Delphi

Figura 8: Exemplo de busca em mltiplas linhas sem o roSingleLine

No obtivemos sucesso, pois a motor RegEx no considerou a quebra


de linha (#13#10) ao fazer o processamento, gerando falha. Para
conseguirmos o nosso objetivo, devemos sinalizar ao motor RegEx para
considerar a quebra de linha no meta-caractere ponto:

Figura 9: Exemplo de busca em mltiplas linhas com o roSingleLine

43 de 68
Expresses Regulares com Delphi

Tipos para Manipulao de ReGex no Delphi

A primeira observao que devemos fazer sobre os tipos que lidam com
Expresso Regular no Delphi XE2 que elas no so classes e sim records com
mtodos. importante perceber isso para no fazermos cdigos
desnecessrios imaginando que devemos necessariamente instanciar estas
supostas classes e consequentemente liber-las da memria.

Vamos aos tipos:

Nome Tipo Propsito


TRegEx Record o tipo responsvel pela aplicao da RegEx em
um alvo
TMatch Record Representa uma combinao
TMatchCollection Record Representa uma coleo de combinaes
TGroup Record Representa um agrupamento
TGroupCollection Record Representa uma coleo de agrupamento
TMatchEvaluator Pointer um ponteiro para um mtodo que ser
acionado a cada combinao com sucesso em
uma operao de replace.

44 de 68
Expresses Regulares com Delphi

Tipo: TRegEx

O tipo TRegEx o record que efetivamente aplica uma Expresso


Regular em um determinado alvo.

Possui vrios mtodos e ao longo dos artigos conhecemos alguns deles.


Esses mtodos por sua vez so sobrecarregados tendo vrias opes de uso.

A primeira dvida recorrente se devemos ou no inicializar o TRegEx. A


princpio no precisamos. S teremos alguma vantagem em faz-lo em caso
de uso repetitivo no mesmo algoritmo. Eu prefiro o termo inicializar ao termo
instanciar, pois como dito anteriormente no se trata de uma classe e sim de
um record com mtodos.

Veja dois exemplos, com e sem a inicializao.

procedure TfExemplo.Button1Click(Sender: TObject);


var
_RegEx : TRegEx;
bRet : Boolean;
begin
_RegEx := TRegEx.Create('Delphi',[]);
bRet := _RegEx.IsMatch('O Delphi sensacional!');
ShowMessage('Sucesso: ' + BoolToStr(bRet,True));
end;
Listagem 1: Exemplo de uso do tipo TRegEx com inicializao

procedure TfExemplo.Button2Click(Sender: TObject);


var
bRet : Boolean;
begin
bRet := TRegEx.IsMatch('O Delphi sensacional!','Delphi',[]);
ShowMessage('Sucesso: ' + BoolToStr(bRet,True));
end;
Listagem 2: Exemplo de uso do tipo TRegEx sem inicializao

O resultado o mesmo. A nica vantagem no caso de uma


inicializao em um cdigo com loop, pois alguns passos seriam poupados.
Na Listagem 3 temos um exemplo onde uma suposta listagem de nomes
analisado dando-se um tratamento especial s linhas que contm o nome
Fernanda:

45 de 68
Expresses Regulares com Delphi

procedure TfExemplo.Button3Click(Sender: TObject);


var
slResultado : TStringList;
_arquivo : TextFile;
_RegEx : TRegEx;
sLinha : string;
bRet : Boolean;
_tempo : TStopwatch;
begin
slResultado := nil;
try
slResultado := TStringList.Create;
_RegEx := TRegEx.Create('\bFernanda\b',[roIgnoreCase,roCompiled]);
AssignFile(_arquivo,'.\listagem.txt');
Reset(_arquivo);
_tempo := TStopwatch.Create;
_tempo.Start;
while not Eof(_arquivo) do
begin
Readln(_arquivo,sLinha);
bRet := _RegEx.IsMatch(sLinha);
if (bRet) then
begin
slResultado.Add(sLinha);
end;
end;
_tempo.Stop;
CloseFile(_arquivo);
finally
if (Assigned(slResultado)) then
begin
Memo1.Text := slResultado.Text;
slResultado.Free;
end;
ShowMessage('Tempo: ' + IntToStr(_tempo.ElapsedMilliseconds));
end;
end;
Listagem 3: Exemplo de uso do tipo TRegEx com inicializao em um loop

Veja que usamos o modificar roCompiled com o objetivo de tornar a


aplicao da RegEx ainda mais rpida. Abordei este assunto no artigo
anterior.

Mtodo: TRegEx.IsMatch

Nos exemplos at aqui usamos o mtodo IsMatch que simplesmente


indica se houve uma combinao (True) ou se no houve combinao
(False). No temos, portanto, nenhuma informao adicional. muito til para
validao. Um uso prtico programar no OnExit de um TEdit por exemplo,
validando um e-mail, endereo IP, URL ou qualquer outra necessidade.

Mtodo: TRegEx.Escape

Retorna a RegEx em questo com os meta-caracteres escapados, ou


seja, precedidos por barra invertida (\). Caso em uma busca voc no queira
considerar os meta-caracteres, mas sim fazer uma busca literal, o mtodo
Escape, ento, facilita esta tarefa.

46 de 68
Expresses Regulares com Delphi

Mtodo: TRegEx.Match

O mtodo Match retorna um TMacth (explicado mais a frente), que


representa uma combinao. Por retornar apenas uma combinao a funo
retorna assim que encontrar a primeira combinao.

Por ser um mtodo sobrecarregado existem algumas possibilidades


interessantes:

function Match(const Input: string): TMatch; overload;

Esta verso til no caso de um TRegEx inicializado pois requer apenas


o texto alvo.

function Match(const Input: string; StartPos: Integer): TMatch; overload;

Esta verso d a possibilidade de aplicarmos a RegEx a partir de uma


determinada posio do texto alvo.

function Match(const Input: string; StartPos, Length: Integer): TMatch; overload;

J esta verso d a possibilidade de limitar a abrangncia de


aplicao da RegEx no texto alvo.

class function Match(const Input, Pattern: string): TMatch; overload; static;

Esta verso pede o texto alvo e a RegEx a ser aplicada.

class function Match(const Input, Pattern: string; Options: TRegExOptions):


TMatch; overload; static;

Por fim, esta verso pede o texto alvo, a RegEx e os modificadores a


serem aplicados.

Mtodo: TRegEx.Matches

O mtodo Matches retorna um TMatchCollection (que ser explicado


mais a frente), ou seja, uma coleo de TMatch ou combinaes. Portanto, o
mtodo retornar apenas quando todo o texto alvo for analisado.

Possui as mesmas possibilidades de TRegEx.Match exceto a de limitar a


abrangncia de aplicao da RegEx no texto alvo (parmetro Length).

Mtodo: TRegEx.Replace

47 de 68
Expresses Regulares com Delphi

O mtodo Replace se mostra extremamente til para busca e


substituio de texto. Muitos argumentam que a funo StringReplace j faz
isso. Mas o detalhe que podemos substituir os textos que combinam com
uma RegEx, enquanto que o StringReplace s trabalha com texto literal.

Assim, como nos outros mtodos existem vrias possibilidades, porm no


nosso exemplo vamos nos concentrar na mais bsica.

Em um novo aplicativo, proponha-se o seguinte layout:

leRegEx:
TLabeledEdit

leNovoTexto:
TLabeledEdit

bProcessar:
TButton

mTextoOriginal: mNovoTexto:
TMemo TMemo

Figura 1: Proposta de layout de aplicativo de estudo

Em seguida o cdigo do evento OnClick do boto bProcessar:

procedure TfPrincipal.bProcessarClick(Sender: TObject);


var
sTexto : string;
sRegEx : string;
sNovoTexto : string;
begin
sTexto := mTextoOriginal.Text; //Texto original
sRegEx := leRegEx.Text; //Expresso regular
sNovoTexto := leNovoTexto.Text; //Novo texto

//Resultado final
mNovoTexto.Text := TRegEx.Replace(sTexto,sRegEx,sNovoTexto);
end;
Listagem 4: Exemplo de utilizao do mtodo Replace

Por fim, um exemplo onde estamos substituindo os nomes Fernanda e


Fernandinha por Fefezinha:

48 de 68
Expresses Regulares com Delphi

Figura 2: Aplicao do mtodo Replace

Vimos acima uma aplicao simples do mtodo replace. Mas existe


alguns mtodos sobrecarregados muito interessantes que pedem um
TMatchEvaluator. Este tipo um ponteiro para um mtodo com a seguinte
assinatura:

function(const Match: TMatch): string

Ela nos permite avaliar a combinao encontrada e determinar por


qual texto ele ser substitudo.

Aproveitando o aplicativo que criamos, vamos adicionar mais um boto


(bEvaluator) e codifica-lo como se segue. O objetivo criarmos meta-tags,
encerrados por sinal de percentual (%) que ser substitudo pelo valor correto.

procedure TfPrincipal.bEvaluatorClick(Sender: TObject);


var
sTexto: string;
sRegEx: string;
sNovoTexto: string;
begin
sTexto := mTextoOriginal.Text;
sRegEx := leRegEx.Text;
sNovoTexto := leNovoTexto.Text;

mNovoTexto.Text := TRegEx.Replace(sTexto, sRegEx, Self.Avaliador);


end;
Listagem 5: Exemplo de utilizao do mtodo Replace com TMatchEvaluator

A seguir, o cdigo do mtodo Avaliador que estamos passando como


parmetro do Replace:

49 de 68
Expresses Regulares com Delphi

function TfPrincipal.Avaliador(const Match: TMatch): string;


begin
if (Match.Value = '%NOME%') then
begin
Result := 'Mrio Guedes';
end
else if (Match.Value = '%EMAIL%') then
begin
Result := 'jmarioguedes@gmail.com';
end;
end;
Listagem 4: Exemplo de um mtodo TMatchEvaluator

Por fim, o exemplo em execuo:

Figura 3: Aplicao do mtodo Replace com um TMatchEvaluator

Mtodo: TRegEx.Split

Com o mtodo Split conseguimos dividir o texto alvo usando uma RegEx
como separador. Ainda usando o nosso aplicativo de estudo, vamos colocar
mais um boto (bSplit) e codificar como segue:

procedure TfPrincipal.bSplitClick(Sender: TObject);


var
sTextoAlvo : string;
sSeparador : string;
aResultado : TArray<string>;
sItem : string;
begin
sTextoAlvo := mTextoOriginal.Text;
sSeparador := leRegEx.Text;

mNovoTexto.Clear;
aResultado := TRegEx.Split(sTextoAlvo, sSeparador);
for sItem in aResultado do
begin
mNovoTexto.Lines.Add(sItem);
end;
end;
Listagem 5: Exemplo de utilizao do Split

50 de 68
Expresses Regulares com Delphi

No exemplo abaixo, estamos utilizando a tag <br> como separador de


campo:

Figura 4: Exemplo de utilizao do Split

Tipo: TMatch

O tipo TMatch representa uma combinao. Este tipo nos oferece as


seguintes informaes:

Propriedade Propsito
Success Indica que houve sucesso na combinao
Index Posio em que a combinao se inicia no texto
alvo
Length Tamanho do texto que combinou com a RegEx
Value Texto que combinou com a RegEx
Groups Coleo de agrupamentos que fazem parte da
combinao

Temos ainda dois mtodos, NextMatch e Result:

Mtodo: TMatch.NextMatch

O mtodo NextMatch nos permite ir avanando nas combinaes


gradualmente, sendo que podemos interromper conforme a necessidade.

51 de 68
Expresses Regulares com Delphi

procedure TfPrincipal.bNextMatchClick(Sender: TObject);


var
sTexto: string;
sRegEx: string;
_match: TMatch;
begin
sTexto := mTextoOriginal.Text;
sRegEx := leRegEx.Text;

_match := TRegEx.Match(sTexto,sRegEx,[]);
while _match.Success do
begin
mNovoTexto.Lines.Add(_match.Value);
_match := _match.NextMatch;
end;
end;
Listagem 6: Exemplo de utilizao do NextMatch

No algoritmo acima estamos buscando apenas cadeias de nmeros


alimentando o TMemo mNovoTexto a cada resultado:

Figura 5: Exemplo de utilizao do NextMatch

52 de 68
Expresses Regulares com Delphi

Mtodo: TMatch.Result

O mtodo Result uma forma de se obter o valor combinado


aplicando-se uma formatao.

Vamos colocar mais um boto (bResult) com o cdigo abaixo:

procedure TfPrincipal.bResultClick(Sender: TObject);


var
sRegEx : string;
sTexto : string;
sFormato : string;
_match : TMatch;
begin
mNovoTexto.Clear;

sRegEx := leRegEx.Text;
sTexto := mTextoOriginal.Text;
sFormato := leNovoTexto.Text;
_match := TRegEx.Match(sTexto,sRegEx,[]);

while (_match.Success) do
begin
mNovoTexto.Lines.Add(_match.Result(sFormato));
_match := _match.NextMatch;
end;
end;
Listagem 7: Exemplo de utilizao do Result

No exemplo abaixo, estamos retornando duas informaes: DDD e


Telefone, porm retornando a informao j formatada. Perceba que a RegEx
possui dois agrupamentos e que no Result estamos fazendo referncia a estes
agrupamentos.

Figura 6: Exemplo de utilizao do Result

53 de 68
Expresses Regulares com Delphi

Tipo: TMatchCollection

O TMatchCollection uma coleo de combinaes no havendo


nada de relevante a ser dito sobre ele, a no ser claro, seu funcionamento
bsico.

Vale mencionar ainda que o mtodo TRegEx.Matches se diferencia do


TMatch.NextMatch justamente pelo fato do primeiro s retornar quando todas
as combinaes forem encontradas, o que pode ser problemtico em textos
muito grandes.

Segue o cdigo de mais um boto, o bMatchCollection, onde varremos


a todas as combinaes retornadas colocando o texto no TMemo
mNovoTexto.

procedure TfPrincipal.bMatchCollectionClick(Sender: TObject);


var
sAlvo : string;
sRegEx : string;
_collection : TMatchCollection;
i : Integer;
begin
mNovoTexto.Clear;
sAlvo := mTextoOriginal.Text;
sRegEx := leRegEx.Text;

_collection := TRegEx.Matches(sAlvo,sRegEx,[]);
if (_collection.Count > 0) then
begin
for i := 0 to Pred(_collection.Count) do
begin
mNovoTexto.Lines.Add(_collection.Item[i].Value);
end;
end;
end;

O cdigo acima usa o for..to..do mas poderamos usar tranquilamente o


for..in..do, como no exemplo abaixo:

procedure TfPrincipal.bMatchCollection2Click(Sender: TObject);


var
sAlvo : string;
sRegEx : string;
_collection : TMatchCollection;
_match : TMatch;
begin
mNovoTexto.Clear;
sAlvo := mTextoOriginal.Text;
sRegEx := leRegEx.Text;

_collection := TRegEx.Matches(sAlvo,sRegEx,[]);
if (_collection.Count > 0) then
begin
for _match in _collection do
begin
mNovoTexto.Lines.Add(_match.Value);
end;
end;
end;

54 de 68
Expresses Regulares com Delphi

Tipo: TGroup

O tipo TGroup prove informaes sobre os grupos presentes em um


determinado TMatch. As informaes que ele oferece so:

Propriedade Propsito
Success Indica o sucesso da combinao pelo agrupamento
Index Indica a posio do texto combinado pelo agrupamento
no texto alvo
Length Indica o tamanho do texto combinado pelo
agrupamento
Value Indica o texto combinado pelo agrupamento

Tipo: TGroupCollection

O tipo TGroupCollection uma coleo de grupos e o seu uso


idntico ao TMatchCollection, no havendo nada a acrescentar.

55 de 68
Expresses Regulares com Delphi

Abordagens Avanadas com RegEx

Grupo de condio: (?(<Retro referncia>)<ER verdadeiro>|<ER


falso>)

Pois . No bastassem todas as possibilidades que vimos at aqui, eis


que surge a de usarmos uma condio booleana: if..then..else.

Nesta construo testado se um grupo anterior combinou ou no. Se


sim, aplicada uma RegEx no restante do texto, seno aplicada outra
RegEx. Perceba que a parte else opcional.

Suponhamos uma lista simples de pessoas com nome, profisso e idade.


Nosso objetivo simplesmente combinar o nome e a idade respectiva de
cada pessoa.

um problema simples, com vrias possibilidades de soluo.

O fato que para o nome a expresso poderia ser algo parecido com
(.*?), enquanto que para a idade o mais adequado (\d*). A primeira
possibilidade que nos surge fazermos dois processamentos individuais. E
para este fato que quero chamar a ateno: o grupo de condio pode
resolver este problema:

Figura 1: Aplicao do grupo de condio

56 de 68
Expresses Regulares com Delphi

Percebemos dois resultados distintos: Um para quando o grupo 1


combinou e outro para quando no combinou. Vamos analisar a RegEx em
questo:

Interpretao tomo
Grupo normal com ou sem
(^NOME:)?
ocorrncia
Incio de grupo modificado (?
Retro referncia ao grupo 1 (1)
RegEx para verdadeiro (.*?),
Indicao da parte else |
RegEx para falso (\d*)
Fim de grupo modificado )

Isso ento abre diversas possibilidades. O nico seno o retorno dos


grupos. Veja que o primeiro resultado retorna dois grupos, ao passo que o
segundo trs grupos. Isso realmente pode ficar confuso na linguagem
hospedeira.

Grupo olhar em volta (look around)

H momentos em que queremos combinar com um determinado


trecho somente se este for precedido ou sucedido por um texto em especfico,
porm no queremos que este texto especfico faa parte da combinao.

Essa facilidade especialmente til em rotinas de substituio de texto.


Temos, ento, quatro possibilidades:

Tipo tomo Explicao


Olhar adiante (look (?=<RegEx>) Bem sucedida se combinar
ahead) direita
Olhar adiante negativo (?!<RegEx>) Bem sucedida se no combinar
direita
Olhar para trs (look (?<=<RegEx>) Bem sucedida se combinar
behind) esquerda
Olhar para trs negativo (?<!<RegEx>) Bem sucedida se no combinar
esquerda

Imaginemos um fragmento HTML simples, que possui vrios elementos e


por algum motivo queremos apenas as que estiverem entre as tags <b> e
<\b>, porm no queremos que estas tags faam parte do resultado. A
soluo a seguinte:

57 de 68
Expresses Regulares com Delphi

Figura 2: Aplicao dos grupos Olhar em Volta

Conseguimos retornar apenas Linha 2 sem incluir as tags. Aqui


usamos o olhar adiante e o olhar para trs positivos, ou seja, a RegEx
combinou quando as condies combinaram.

Mas pode ser que no desejamos uma combinao, quando esta for
precedida ou sucedida por um determinado valor. Vamos imaginar uma lista
de nome e sobrenome onde queremos retornar apenas o sobrenome de uma
determinada pessoa:

58 de 68
Expresses Regulares com Delphi

Figura 3: Aplicao dos grupos Olhar em Volta

E se quisermos o resultado inverso basta uma mudana simples:

Figura 4: Aplicao dos grupos Olhar em Volta

Isto tudo, se aplicado a uma rotina de substituio de texto, faz com


que este processo seja cirrgico, pois conseguiramos, por exemplo, mudar de
GUEDES para SILVA somente as entradas realmente desejadas.

Vale agora uma observao muito importante: Estas construes no


consome o texto combinado. No exemplo abaixo, usei o GUEDES duas vezes.
No que seja uma construo exatamente til, mas exemplifica bem o
conceito. A construo foi necessria, pois o GUEDES no foi consumido pelo
(?=GUEDES):

Figura 5: Aplicao dos grupos Olhar em Volta

59 de 68
Expresses Regulares com Delphi

Por fim, encerrando este assunto, podemos usar os grupos olhar em


volta em um grupo condicional. Se a combinao for bem sucedida aplica-
se uma RegEx, enquanto que se no for bem sucedida aplica-se outra RegEx.

60 de 68
Expresses Regulares com Delphi

Abreviaes de caracteres

Obviamente alguns caracteres so difceis de representar, como a


quebra de linha por exemplo. Segue uma tabela com os meta-caracteres
especiais que representam esses casos:

Abreviao Significado
\a Alerta
\b Backspace
\e Escape
\f Alimentador
\n Quebra de linha
\r Retorno de carro
\t Tabulao
horizontal
\v Tabulao
vertical

De toda a tabela certamente usaremos com mais frequncia o \r\n


que equivale a uma quebra de linha padro no Windows, e que no Delphi
equivalente #13#10.
Mas tome um cuidado especial com estas abreviaes especialmente
se estiver analisando texto gerados em outros sistemas operacionais.

Abreviaes de classes de caracteres

Algo importante a ser considerado na construo de uma RegEx a


limitao de abrangncia de um determinando tomo. Se quisermos, por
exemplo, que uma posio case apenas com nmeros de zero a nove
poderamos usar:

(0|1|2|3|4|5|6|7|8|9)

Isso no parece agradvel de ler nem muito menos de aplicar. Com a


classe de caractere a RegEx passa a ser:

[0123456789]

Melhorou. Mas que tal esta construo agora?

[0-9]

Ficou bem melhor. Mas, para este caso em especfico, ou seja,


combinar com qualquer nmero de zero a nove, pode-se usar simplesmente
uma abreviao de classe para dgitos:

61 de 68
Expresses Regulares com Delphi

\d

Repare que o ponto, que casa qualquer caractere, por si s uma


abreviao de classe. equivalente a algo como:

[abcdefghijklmnopqrstuvwxyzABC ...]

Exatamente: No fosse o ponto e seriamos obrigados a digitar todos os


caracteres possveis em uma determinada posio. Mas existem outras
abreviaes de classe, a seguir conheceremos as mais usuais:

Abreviao Significado
\d Dgito, equivalente [0-9]
\D No dgito, equivalente [^0-9]
\w Caractere de palavra, equivalente [a-ZA-Z0-9_]
\W Caractere de no palavra, equivalente [,.!@#$ ...]
\s Caractere de espao em branco, equivalente [
\f\n\r\t\v]
\S Caractere no de espao em branco, equivalente
[a-zA-Z...]

Com isso, podemos refinar ainda mais a nossa busca sem maiores
obstculos como tempo e tamanho da RegEx.

Problemtica de acentuao e outras lnguas

Um problema recorrente que temos com RegEx com acentuao.


Fica muito ruim de repente ficar declarando classes de caracteres para
[] e por ai vai. Isso acontece porque o motor RegEx trabalha
essencialmente com a tabela ASCII, do byte 0 ao byte 127 (7 bits), deixando
de fora, entre outros, os caracteres acentuados.

No exemplo que segue temos a palavra CORAO e percebemos que o


e o no fazem parte do resultado:

Figura 6: Aplicao da abreviao de classe \w

62 de 68
Expresses Regulares com Delphi

um problema srio, que muitas vezes solucionado com o meta-


caractere ponto. Mas o ponto pode ser abrangente demais. Para resolver isto
temos duas solues possveis: as classes de caracteres POSIX (que no
funciona no Delphi) ou trabalhar diretamente com as propriedades Unicode.

63 de 68
Expresses Regulares com Delphi

Classes de caracteres POSIX

As classes de caracteres POSIX infelizmente no funcionam a contento


no Delphi, mas pode ser uma soluo em outras ferramentas. As classes POSIX
se baseiam nas configuraes regionais do sistema operacional corrente para
determinar o que so dgitos, letras, pontuao e etc.

Segue uma tabela com as classes POSIX mais comuns:

POSIX Significado
[:upper:] Letras maisculas
[:lower:] Letras minsculas
[:alpha:] Letras
[:alnum:] Letras e nmeros
[:digit:] Nmeros
[:xdigit:] Nmeros hexadecimais
[:punct:] Sinais de pontuao
[:blank:] Espao e tabulao
[:space:] Caracteres em branco (espao, tabulao, retorno de
carro e etc.)
[:cntrl:] Caracteres de controle
[:graph:] Caracteres imprimveis
[:print:] Caracteres imprimveis e o espao

Perceba que uma classe POSIX deve ser usada dentro de uma classe
de caracteres maior, como no exemplo abaixo:

Figura 7: Aplicao da abreviao de classe POSIX [[:alpha:]]

Ainda no exemplo acima percebe-se que o [:alpha:] no incluiu o


e o , como deveria.

64 de 68
Expresses Regulares com Delphi

Trabalhando com Unicode

A soluo mais segura para trabalhar com caracteres que no esto na


tabela ASCII bsica utilizao da tabela Unicode (http://www.unicode.org).

A tabela Unicode pode representar todos os caracteres concebidos


pela humanidade. Muito alm de representar os caracteres, a codificao
Unicode prev os atributos destes caracteres, como caixa alta ou caixa baixa
por exemplo.

Para trabalharmos com os caracteres na tabela Unicode usamos o


meta-string \p{atributo} para combinar com caracteres que possui o
atributo em questo e \P{atributo} para combinar com caracteres que no
possui o atributo em questo.

Solucionando, ento, o problema com a palavra CORAO temos:

Figura 8: Aplicao de uma meta-string para Unicode

A propriedade \p{Lu} se refere s letras maisculas: L de letter, u de


upper.

Perceba que se precisar lidar com escritos em outras lnguas, a soluo


passar, necessariamente, pelo Unicode.

Por falar em outras lnguas, segue um exemplo de aplicao da RegEx


em um texto rabe:

Figura 9: Exemplo de um texto em rabe

65 de 68
Expresses Regulares com Delphi

O texto Isto est escrito em rabe e a RegEx escrito.


Mas, e se precisarmos destacar cada palavra do texto? O \w invivel neste
caso. Usamos, ento, a RegEx \p{L}* que simplesmente significa: uma letra
em qualquer quantidade:

Figura 10: Exemplo de um texto em rabe com a aplicao de um meta-string


Unicode

Segue uma tabela com alguns meta-strings interessantes para se


trabalhar com Unicode:

Meta- Significado
string
\p{L} Smbolos considerados letras.
\p{Lu} Smbolos considerados letras maisculas
\p{Ll} Smbolos considerados letras minsculas
\p{M} Smbolos que devem vir sobre outro caractere base, como
acento.
\p{Z} Smbolos de separao, mas sem representao visual
(espao).
\p{S} Smbolos diversos como cifro e trema.
\p{N} Smbolos que representam nmeros.
\p{P} Smbolos que representam pontuao como exclamao e
interrogao.
\p{C} Smbolos diversos.

Esta tabela bsica. Existem dezenas de possibilidades.

66 de 68
Expresses Regulares com Delphi

Escape octal - \<nmero>

Ainda na linha dos caracteres difceis de representar, caso voc se


depare com um texto com caracteres estranhos, mas que influencia no seu
resultado, voc pode buscar este caractere pelo seu cdigo na tabela ASCII,
porm na base octal. No exemplo que segue o texto foi colocado do MS Excel
e ao invs de espaos temos um caractere de tabulao:

Figura 11: Texto colocado a partir do MS Excel e uma RegEx que no combina

Perceba na imagem que a RegEx NOME:[ ]+ no combina, apesar de


aparentemente haver espaos aps NOME: .

Mas na verdade o que existe um caractere de tabulao. Ento


podemos mudar a RegEx para:

Figura 12: RegEx utilizando um escape octal

Poderamos ter usado o \t para o mesmo efeito, mas usamos \011


(que equivale a nove na base octal) para demonstrar a facilidade.

67 de 68
Expresses Regulares com Delphi

ncoras e outras asseres de largura zero

Finalizando o artigo, vamos falar sobre as ncoras. Os meta-caracteres


do tipo ncora no combinam com um texto propriamente dito, mas sim com
uma posio no texto. Neste grupo cabe inclusive os grupos olhar em volta
do qual falamos no comeo deste artigo.

J conhecemos o circunflexo e o cifro que combinam com o incio e


fim de linha respectivamente. Porm, o comportamento destes meta-
caracteres muda com o uso do modificador roMultiline e roSingleLine, o
que pode prejudicar algum objetivo. Para resolver isto temos as seguintes
meta-strings:

Meta- Objetivo
String
^ Combina com o incio de uma string ou linha, de acordo com
o modificador.
$ Combina com o final de uma string ou linha, de acordo com o
modificador.
\A Combina sempre com o incio de uma string.
\Z Combina sempre com o final de uma string.
\z Combina sempre com o final de uma string.
\b Combina com borda de palavra.
\B Combina com no borda de palavra.

68 de 68

Вам также может понравиться