Sei sulla pagina 1di 9

Expresses Regulares (Armamento Pesado) Publicado em: 22/09/2008 Compartilhe

Hoje gostaria de falar sobre um assunto que gosto muito e que me encantei desde que aprendi na faculdade: Expresses Regulares. S que, antes disso, deixe-me mostrar que h muito tempo j tivemos algum tipo de contato com este recurso, s que de uma forma mais simples e indiretamente. No post de hoje quero mostrar o poder das expresses regulares, ou RegEx. Isso d assunto para livro de mais de 1000 pginas e no post de hoje pretendo mostrar os conceitos bsicos. Sempre que estudo mais um pouco descubro que no conheo nada e que preciso praticar ainda mais e mais... Isso mesmo, tem que praticar muito e muito. Sempre que possvel tento estudar e aprender mais um pouco... Eu disse que voc j conhece este conceito, certo? Ento, lembra-se daqueles comandos que voc executa no MS-DOS para listar os arquivos de um diretrio, o DIR? Por exemplo, C:\Temp>DIR, onde lista todo contedo de um diretrio. S que, algumas vezes, voc necessitou melhorar sua consulta e usou alguns caracteres especficos (ou coringas) para dizer o comando o padro de nome que voc est procurando, por exemplo: * e ?. Lembra? Ento, neste momento voc j identificou o padro do que procura e vai definir uma string que represente este padro, conforme exemplos abaixo: Quando voc usa estes comandos, basicamente, voc identifica um padro atravs de um conjunto de caracteres e, em seguida, usa a string como entrada para uma mquina especial que sabe como executar este padro contra uma fonte de dados, que pode ser um texto, uma lista etc. Ao usar estes comandos no MS-DOS, voc est limitado ao conjunto de caracteres deste ambiente. Com o que vou mostrar hoje, voc ganhar uma super lista com vrios caracteres para representar qualquer padro que imaginar. As expresses regulares oferecem um mecanismo poderoso, flexvel e eficiente para processamento de texto. Atravs de uma notao extensa possvel analisar uma grande massa de dados a procura de padres: para extrao, editar e substituir textos de uma string. essencial para aplicaes que lidam com processamento de texto, tais como: processadores HTML, analisadores de Log, analisadores de cabealho HTTP, entre outros. Vou falar sobre expresso regular com Visual C#, mas saiba que pode us-la em qualquer linguagem que tenha suporte a tal. Por exemplo, voc pode usar e abusar de expresso regular com JavaScript, para mais informaes acesse o site Core JavaScript 1.5 Reference:Global Objects:RegExp. Para expresses regulares com VBScript, acesse o site Microsoft Beefs Up VBScript with Regular Expressions. O site Regular-Expressions.Info tambm contm muito material interessante. Uma ferramenta para design e teste da Expresso Regular Antes de voc comear a construir um cdigo que far uso de uma expresso regular, acredito que o mais importante que voc faa uma anlise do padro que deseja capturar e, em seguida, faa o design da sua expresso. Assim que tiver sua expresso pronta, teste-a para garantir que todas as entradas sejam capturadas completamente. Ento, para projetar e testar sua expresso, nada melhor do que utilizar o RegExDesigner.NET do SellsBrothers. Com ela voc pode testar expresses para combinao, substituio e diviso de strings. Alm disso, tem suporte para gerao do cdigo, em VB.NET ou C#, do que foi preenchido nos campos da tela. Ou seja, facilmente voc j tem o cdigo na sua linguagem favorita pronto para inserir no seu programa. O legal desta ferramenta que voc pode pensar na expresso abstraindo os aspectos da linguagem, mantendo o foco s na expresso. Voc tambm pode testar suas expresses na ferramenta online do RegExLib.Com. Veja a figura abaixo, um exemplo da IDE do RegExDesigner, onde eu tenho uma expresso "Renato" que desejo validar contra a string "Renato Guimares, Microsoft MVP Visual C#". Mas a palavra "Renato" uma expresso? Sim. Ou seja, voc est dizendo que o padro procurado "Renato", s que este exemplo no nada frente ao poder dos meta-caracteres das expresses regulares. Clique sobre o botao "Match Text" para ferramenta executar a expresso. Caso queira testar a substituio, clique em "Replace Text". Perceba do lado direito onde a ferramenta mostra na cor verde parte da string que combinou com o padro da expresso. Outro exemplo de uso da ferramenta, caso queira testar uma expresso para substituio, s informar o texto na aba "Replacement String". No caso da substituio, a string resultante

ser substuda em todas as partes onde o padro combinou, o que no exemplo s aconteceu na primeira palavra do texto de entrada. Um outro recurso interessante desta ferramenta a gerao de cdigo com base no que foi informado na interface grfica. Por exemplo, veja o cdigo C# gerado pela ferramenta para o exemplo anterior: using System.Text.RegularExpressions; // Regex Match code for C# void MatchRegex() { // Regex match RegexOptions options = RegexOptions.None; Regex regex = new Regex(@"Renato", options); string input = @"Renato Guimares, Microsoft MVP Visual C#"; // Check for match bool isMatch = regex.IsMatch(input); if( isMatch ) { // TODO: Do something with result System.Windows.Forms.MessageBox.Show(input, "IsMatch"); } // Get match Match match = regex.Match(input); if( match != null ) { // TODO: Do something with result System.Windows.Forms.MessageBox.Show(match.Value, "Match"); } // Get matches MatchCollection matches = regex.Matches(input); for( int i = 0; i != matches.Count; ++i ) { // TODO: Do something with result System.Windows.Forms.MessageBox.Show(matches[i].Value, "Match"); } // Numbered groups for( int i = 0; i != match.Groups.Count; ++i ) { Group group = match.Groups[i]; // TODO: Do something with result System.Windows.Forms.MessageBox.Show(group.Value, "Group: " + i);

// Named groups string groupA = match.Groups["groupA"].Value; string groupB = match.Groups["groupB"].Value; // TODO: Do something with result System.Windows.Forms.MessageBox.Show(groupA, "Group: groupA"); System.Windows.Forms.MessageBox.Show(groupB, "Group: groupB");

Como est o suporte do .NET a expresso regular? O .NET Framework incorpora as caractersticas mais populares dos principais mecanismos de expresso regular: Perl e awk. Foi projetado para ser compatvel com o mecanismo de expresso regular do Perl 5.0. Alm disso, tambm implementa caractersticas ainda no vista em outras implementaes, tais como combinao right-to-left e compilao on-the-fly. O mecanismo de RegExp do .NET faz parte das classes bsicas e pode ser usado por qualquer linguagem ou ferramenta que suporte o CLR, inclusive ASP.NET e Visual Studio .NET. As classes esto no namespace System.Text.RegularExpressions, tendo como principais classes Regex, Match, MatchCollection, GroupCollection, CaptureCollection, Group e Capture. Voc pode criar uma instncia desta classe ou usar algum dos seus mtodos estticos, por exemplo, o mtodo Match(). using System; using System.Text.RegularExpressions; namespace PostsMSN.Samples.RegularExpression { class Program{ static void Main(string[] args){ //String usada como entrada string input = "Renato Guimares"; //Executa o Match para procurar a combinao com base no padro Match match = Regex.Match(input, "Renato"); //Se o mtodo Match foi bem sucedido, retorna a posio e o texto encontrado if (Regex.Match(input, "Renato").Success){ Console.WriteLine("Posico: " + match.Index + " Texto: " + match.Value); } //OBS: Pode-se usar o mtodo Match, visto que o padro pode ser encontrado //mais de uma vez na string de entrada. } } } //Resultado => Posico: 0 Texto: Renato

O Visual Studio .NET possui uma documentao excelente sobre Expresses Regulares, to bom quanto um livro. Por exemplo, caso quira saber os detalhes do funcionamento do mecanismo no .NET, procure no help do VS.NET por "Details of Regular Expression Behavior". Recomendo tambm a leitura do tpico "Compilation and Reuse", tambm do tutorial do VS.NET. Por exemplo, alerta que o uso da opo RegexOptions.Compiled deve ser feita com cautela, pois os recursos usados para gerao de cdigo para melhorar performance no so liberados quando a instncia de Regex criada for removida da memria, por exemplo. Quais so os caracters que posso usar numa expresso?

J falei demais e ainda no mostrei os operadores, caracteres e construres que podem ser usados na definio de uma expresso regular. Sendo assim, antes de vermos qualquer outro exemplo, vamos conhecer as tabelas dos caracteres de escape, classes de caracteres, quantificadores, agrupadores, referncia e alternao. Antes disso, saiba que qualquer outro caractere que no seja um destes (. $ ^ { [ ( | ) * + ? \ ), so considerados como o prprio caractere. Por exemplo, no comeo do post coloquei um exemplo com a expresso regular "Renato", ou seja, cada caractere representa o seu prprio significado. Caracteres de Escape: Caracteres que tm significado especial quando so precedidos pelo caractere "\". Classes de Caracteres: Para simplificar o post, esta tabela foi resumida para ilustrar somente as classes usadas nos exemplos. Para mais detalhes, consultar documentao do Visual Studio .NET. Na tabela abaixo perceba que uma classe em letra minscula tem seu inverso usando a mesma classe em letra maiscula. Alm disso, perceba que o caractere "^" usado para negar o contedo de um grupo.

Afirmaes de posio: Caracteres que indicam se a validao foi bem sucedida, ou no, dependendo da posio corrente da string. Perceba que o caractere "^", neste caso tem um significado diferente da tabela acima (onde ele deve acontecer entre []). Quantificadores: So usados para indicar a quantidade de vezes que um padro deve acontecer. Podem ser aplicados a um caractere, um grupo ou a uma classe de caracteres. Na tabela abaixo, n e m so nmeros inteiros. Agrupadores: So usados para definir sub-expresses de uma expresso regular e capturar substrings da string de entrada. Estes agrupadores podem ser nomeados ou, caso no receba um nome especfico, recebero um nome padro. Alm disso, so numerados sequencialmente, com base na ordem de abertura dos parnteses da esquerda para direita, porm a numerao dos agrupamentos nomeados s se inicia aps os grupamentos no nomeados. Por exemplo, na expresso ((?<One>abc)\d+)?(?<Two>xyz)(.*) os agrupamentos so capturados por nome e nmero. O agrupamento zero (a primeira captura) sempre para expresso toda (exemplo do Visual Studio). Nmero: 0 Nome: default Padro: ((?<One>abc)\d+)?(?<Two>xyz)(.*) Nmero: 1 Nome: default Padro: ((?<One>abc)\d+) Nmero: 2 Nome: default Padro: (.*) Nmero: 3 Nome: One Padro: (?<One>abc) Nmero: 4 Nome: Two Padro: (?<Two>xyz)

Vamos praticar alguns exemplos com as tabelas acima Aps elaborar uma expresso, importante que voc defina uma massa de dados para testar os casos onde a expresso combina ou no o padro. Alm disso, importante lembrar que no existe somente uma forma de definir uma expresso, por exemplo, no existe s uma forma possvel de montar uma expresso que combine um nmero de telefone, pois tudo depender do padro interessado. Outra coisa importante, expresso regular igual a construo de um trecho de cdigo, voc pode ter n formas de escrever, porm cada uma tem sua lgica. Em resumo, quero dizer que no h s uma expresso correta, pois elas s diferem no nmero de padres que podem capturar. Na figura abaixo, veja que montei uma expresso para capturar uma string no formato de CEP e, logo na caixa abaixo, inseri alguns exemplos para validao da expresso. O cep pode conter, ou no, o "." e o "-" para definio da mscara. Do lado direito da figura em vermelho e verde esto as strings que combinaram com o padro da expresso. Para ilustrar mais exemplos de expresses, criei uma classe com alguns mtodos para validao de formato, veja cdigo abaixo. No caso do Visual C#, como o caractere "\" tem significa especial, indicao de espace, ou voc acrescenta uma outra "\" ou adiciona um "@" (Verbatim String) antes da string.

using System; using System.Text.RegularExpressions; namespace PostsMSN.Samples.RegularExpression{ /// <summary> /// Exemplos de Expresses Regulares para validao de dados /// </summary> public class StringUtil{ /// /// /// /// <summary> Verifica se um nmero de telefone est no formato vlido, inclusive cdigo do pas e da cidade. </summary>

/// <param name="input">Nmero do telefone</param> /// <returns>Verdadeiro se o nmero estiver no formato vlido</returns> public static bool IsTelefoneValido(string input) { string pattern = @"^\+?\d{2,3}?\s*\(\d{2}\)\s*\d{4}-\d{4}$"; return Regex.IsMatch(input, pattern); } /// <summary> /// /// /// /// Verifica se o CEP est no formato correto. </summary> <param name="input">Nmero do cep</param> <returns>Verdadeiro se estiver no formato correto</returns>

public static bool IsCepFormatoValido(string input){ string pattern = @"^\d{2}[\.]?\d{3}-?\d{3}$"; return Regex.IsMatch(input, pattern); } /// <summary> /// Verifica se o CPF est no formato correto, mas no testa se vlido. /// </summary> /// <param name="input">Nmero do cpf com a mscara</param> /// <returns>Verdadeiro se estiver no formato correto</returns> public static bool IsCpfFormatoValido(string input){ string pattern = @"^\d{3}\.\d{3}\.\d{3}-\d{2}$"; return Regex.IsMatch(input, pattern); } /// <summary> /// /// /// /// Verifica se a hora est no formato correto. </summary> <param name="input">Hora no formato HH:MM</param> <returns>Verdadeiro se estiver no formato correto</returns>

public static bool IsHoraFormatoValido(string input){ string pattern = @"^([0-1][0-9]|[2][0-3]):([0-5][0-9])$"; return Regex.IsMatch(input, pattern); }

/// <summary> /// Verifica se e-mail est no formato vlido /// </summary> /// <param name="input">e-mail</param> /// <returns>Verdadeiro se estiver no formato correto</returns> public static bool IsEmailFormatoValido(string input){ string pattern = @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9] {1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"; return Regex.IsMatch(input, pattern); } /// <summary> /// /// /// /// Verifica se o GUID est no formato vlido </summary> <param name="input">GUID com ou sem chaves {GUID}</param> <returns>Verdadeiro se estiver no formato correto</returns>

public static bool IsGuidFormatoValido(string input){ string pattern = @"^[{|\(]?[0-9a-fA-F]{8}[-]?([0-9a-fA-F]{4}[-]?){3}[0-9afA-F]{12}[\)|}]?$"; return Regex.IsMatch(input, pattern); } /// <summary> /// Remove os caracteres especiais de uma string /// </summary> /// <param name="input"></param> /// <returns></returns> public static string LimparString(string input){ string pattern = @"[^\w\.@-]"; return Regex.Replace(input, pattern, ""); } } }

O programa usado para testar as expresses da classe acima: using System; using System.Text.RegularExpressions; namespace PostsMSN.Samples.RegularExpression{ class Program{ static void Main(string[] args){ //Testa o formato do telefone string input = "+55 (81) 3411-9337"; Console.WriteLine("Teste Fone: " + StringUtil.IsTelefoneValido(input)); //Testa o formato do CEP input = "01.522-002"; Console.WriteLine("Teste Cep: " + StringUtil.IsCepFormatoValido(input)); //Testa o formato do CPF

input = "987.980.098-09"; Console.WriteLine("Teste CPF: " + StringUtil.IsCpfFormatoValido(input)); //Testa o formato da hora input = "23:59"; Console.WriteLine("Teste Hora: " + StringUtil.IsHoraFormatoValido(input)); //Testa o formato do e-mail input = "renato.guimaraes@gmail.com"; Console.WriteLine("Teste Email: " + StringUtil.IsEmailFormatoValido(input)); input = "{914D226A-2F5B-4944-934D-96BBE6571977}"; Console.WriteLine("Teste GUID: " + StringUtil.IsGuidFormatoValido(input)); input = "teste#$98#%"; Console.WriteLine("Resultado Limpar: " + StringUtil.LimparString(input)); Console.ReadLine(); } } }

Falando um pouco sobre uso deste poderoso mecanismo no dia-a-dia, outro dia precisava construir um programa que obtivesse um pgina HTML de um determinado endereo e, em seguida, extrasse todas as notcias. Para isso, identifiquei como estava o padro do HTML e localizei a regio de demarcava as notcias e montei uma string que recuperava a regio do documento. Depois, montei uma outra expresso que recuperava o contedo entre um os elementros "<span>" e "</span>" onde o atributo class tivesse um valor especfico (poderia ter matado tudo numa nica expresso). Um outro caso interessante foi quando fiz um prottipo que faz consultas usando os servios do Serasa, onde o resultado vem num determinado padro, minha expresso capturava cada parte do retorno e, em seguida, eu processava o trecho conforme o layout. Para fechar, um outro exemplo, fiz um programa que analisava os arquivos de log do Sharepoint e retornava os erros de um determinado tipo, por exemplo, erros de workflow. Abaixo segue mais dois exemplos interessantes (do Visual Studio .NET) que mostrar a utilizao de expresses regulares com agrupamento. using System; using System.Text.RegularExpressions; namespace PostsMSN.Samples.RegularExpression{ class Program{ static void Main(string[] args){ //Entrada para validacao da expressao que extrai o target="_blank" href String input = "<div>" + "<a target="_blank" href=\"https://www.msdnbrasil.com.br/cadastro/default.aspx\">" + "<img alt=\"Meu MSDN\" src=\"ms348103.my_msdn2_BR.jpg\" /></a>" + "</div>"; //No detalhe, a expresso significa que a string capturada deve: // a) iniciar por target="_blank" href (target="_blank" href); // b) ser seguida por um sinal de igual, e pode ter nenhum ou // vrios espaos entre eles (\\s*=);

no

// c) depois do sinal de igual pode ter nenhum ou vrios espaos (\\s*); // d) seguida pelo agrupamento (?:\"(?<1>[^\"]*)\"|(?<1>\\S+)), mas

// ser capturado // e) no agrupamento <1> pode ter nenhum ou vrios caracteres que no // seja um " ([^\"]*), seguido por um " // f) ou o grupo <1> pode ser um caracter que no seja um espao(\\S+) Regex r = new Regex("target="_blank" href\\s*=\\s*(?:\"(?<1>[^\"]*)\"| (?<1>\\S+))"); for (Match m = r.Match(input); m.Success; m = m.NextMatch()){ Console.WriteLine("target="_blank" href encontrado: " + m.Groups[1] + " na posio " + m.Groups[1].Index); } para //A expresso abaixo usa o recurso de agrupamento com balanceamento // validar se uma expresso matmatica contm um ")" para cada "(". // Perceba que a expresso no valida se s tem nmeros e // os operadores possveis. string pattern = @"^[^\(\)]*(((?'Abre'\()[^\(\)]*)+((?'Fecha-Abre'\))"+ @"[^\(\)]*)+)*(?(Abre)(?!))$"; input = "(10 + 10) * (10-5)"; //input = "(10 + 10) * (10-5))"; Falha porque sobra um ")" Match match = Regex.Match(input, pattern); if (match.Success == true) Console.WriteLine("Entrada: \"{0}\" \nCombinao: \"{1}\"", input, match); else Console.WriteLine("Combinao Falhou."); Console.ReadLine(); //Resultado: // target="_blank" href encontrado: https://www.msdnbrasil.com.br/cadastro/default.aspx // na posio 14 // Entrada: "(10 + 10) * (10-5)" // Combinao: "(10 + 10) * (10-5)" } } }

Fico por aqui e minha recomendao que voc tente aplicar este recurso sempre que possvel, pois facilita e reduz a quantidade de cdigo para rotinas complexas. Lgico, o que falei aqui no nada frente ao mundo de coisas que pode se escrever sobre expresses regulares, pois existe muita teoria (Autmatos Determinsticos, Autmatos No-Determinsticos, Mquina de Turing, Linguagens Regulares, entre outros) por trs destes mecanismos. Pode-se dizer a construo de uma expresso uma forma de programar e o limite est a cargo da sua imaginao. Cada vez que tento entender algum exemplo avanado que pego pela Internet fico admirado com o que possvel fazer... Uma boa forma de aprender analisar exemplos e tentar entender o que faz cada coisa da expresso do exemplo. De primeira vez no fcil montar uma expresso mas com alguma prtica fica fcil entender como funciona e depois s

praticar, praticar e praticar... Assim voc virar um mestre no assunto(eu no sou porque no pratico tanto). Referncias Abrao, .NET Framework Regular Expressions (documentao Visual Studio .NET) Regular-Expressions.Info Exemplos de Expresses Regulares no Regular-Expressions.Info RegExLib.com Exemplos de Expresses Regulares no RegexLib.com

Renato Guimares, MS MVP C#

Potrebbero piacerti anche