Clique aqui para ler todos os artigos desta edição
Expressão Regular com .NET
por Renato Guimarães
Quer reduzir dezenas de linhas de código por apenas uma string com alguns caracteres? Por exemplo, escreva uma função para validar e-mail, e verifique a quantidade de linhas para implementação. Cerca de 10 linhas, certo? Trabalhando com expressões regulares você pode fazer em uma linha o que não conseguiria fazer em dezenas de linhas de código.
Nesse artigo, você terá uma visão geral e aprenderá a base de expressão regular. Para isso, é preciso entender os significados dos meta-caracteres, aprender a escrever expressões e entender como tirar vantagem das classes do .NET Framework que suportam a expressão regular.
Entendendo uma expressão regular
Expressões Regulares são uma poderosa, flexível e eficiente linguagem para descrever e processar texto. O extensível padrão de combinação das expressões regulares permite analisar rapidamente grandes textos para procurar combinações específicas de caracteres, podendo extrair, editar, validar, formatar, substituir ou excluir substrings. Uma expressão regular é formada por dois tipos básicos de caracteres: literais e meta-caracteres. As mais simples são formadas por literais, por exemplo "a" ou por literais e meta-caracteres: "[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?”.
Existe um grupo de caracteres que é responsável pelo poder de processamento de uma expressão regular, ou seja, possuem algum significado especial para o Parser (máquina ou engine - Classes do .NET Framework), sendo: $ ^ . { [ ( | ) ] } * + ? \ . O * (asterísco), + (sinal de adição) e ? (interrogação) são conhecidos como quantificadores porque afetam o número de vezes que uma expressão deve coincidir. Outra forma de especificar o número de combinações é implementada usando as chaves "{...}". A expressão que antecede esses quantificadores normalmente é um simples caracter, por exemplo, "a*", ou pode vir após os parênteses, por exemplo, "(....)*". Os parênteses são utilizados para agrupar expressões; e o “|”(pipe) é utilizado para indicar que a coincidência será com a expressão à esquerda ou à direita do pipe, por exemplo “a(bc|cd)e”, coincidirá com “abce”, “acde”. Veja exemplos de quantificadores na Tabela 1.
Expressão |
Coincidência |
Significado |
maria |
maria |
Coincide somente o literal da expressão |
ca*sa |
csa, casa, caasa, etc |
Nenhuma ou várias coincidências |
ca+sa |
casa, caasa, caaaasa, etc |
Uma ou várias coincidências |
ca?sa |
csa ou casa |
Nenhum ou uma coincidência |
ca{2}sa |
caasa |
Exatamente duas coincidências |
ca{0,}sa |
csa, casa, caasa, caaasa, etc |
Nenhuma ou várias. Igual a * |
ca{1,}sa |
casa, caaasa, caaaasa, etc |
Uma ou várias. {1,} é igual a + |
ca{0,1}sa |
csa ou casa |
Nenhuma ou somente uma. Igual a ? |
Tabela 1 - Exemplos de como utilizar os quantificadores *, + , ?, {n}, {n, } e {n, m}
Classe de caracteres [...], O ponto "." e notações resumidas (atalhos)
O mais simples dos meta-caracteres é o ponto ".". Coincide com qualquer caracter, exceto os de retorno de carro(\r) e nova linha (\n).
Classe de caracter é um forma de você definir um conjunto ou intervalo possível de valores através de [...](colchetes). Sendo assim, "b[aei]d" coincidirá com 'bad", "bed" ou “bid”, mas não com "bod" ou "bud". Tudo que estiver entre colchetes é entendido como caracter único, então "[da]+" coincidirá com "d", "a", "da", "dad", "daaad", "ddaaddadaa" ou qualquer combinação entre d e a. Você pode definir um intervalo usando o hífen: "a[a-c]" coincidirá com "aa", "ab", "ac" mas não com "ad" ou qualquer letra fora do invervalo a-c. Dentro de uma mesma classe podemos definir vários intervalos e/ou caracteres singulares: "[A-Z a-z $ _ 0-9]" coincidirá com qualquer série de letras, maiúsculas ou minúsculas, cifrão, número ou sublinhado. Para negar uma classe de caracteres é preciso colocar um "^" após o "["(abre colchete) : "a[^a-z]" coincidirá com "a1", "a3", ou seja, um "a" seguido de um caracter que não seja uma letra minúscula, assim, não coincidirá, por exemplo, com “ab”. Para restringir uma expressão regular de modo que ela coincida somente com um exato padrão ou com um determinado padrão que apareça no começo ou no fim de um texto grande, você pode usar “^” e “$” para coincidir o começo e o fim do texto, respectivamente. A expressão “^a[^a-z]” coincide com qualquer palavra que comece por “a” não seguido de uma letra minúscula: “a1zzzz”, “a555zds”. a expressão “a[^a-z]$” coincide com qualquer palavra que termine por um “a” não seguido de letra uma minúscula: “a5a5a5a555za1”.
Outra forma de escrever uma classe de caracteres é através das notações resumidas (ou atalhos), que podem ser utilizadas individualmente ou dentro de outra classe de caracteres. Por exemplo, “\d” e “[\d]” são equivalentes a “[0-9]”. Observe que um atalho coincidirá com apenas um único caracter, da mesma forma que ocorre com as classes definidas com colchetes. Veja exemplos de atalhos na Tabela 2.
\d |
Coincidirá com dígitos de 0-9 |
\D |
Coincidirá com qualquer caracter que não seja \d. [^0-9] |
\w |
Coincidirá com caracteres para formar palavras. [a-z A-Z _ 0-9] |
\W |
Coincidirá com qualquer caracter que não seja \w. [^ a-z A-Z _ 0-9] |
\s |
Coincidirá com os caracteres de espaço. [\n \f \t \v] |
\S |
Coincidirá com os caracteres que não seja \s. [^\n \f \t \v] |
Tabela 2 – Atalhos utilizados para representar classes de caracteres
OBS: Alguns caracteres têm um significado especial numa classe de caracteres. Caso utilize como um literal, fique atento, pois precisam ser imediatamente precedidos por uma contra-barra. Exemplos: "\\" (contra-barra), "]" (fecha colchete), "^" (circunflexo) e o "-" (hífen"). Em C++ e C# você deve anteceder a “\” (contra-barra) com outra contra-barra para que o compilador não entenda como um caracter de escape (atalho), por exemplo “\\d”, mas em C#, além da contra-barra você também pode colocar um @ antes da string. Usando o @ você está dizendo para o compilador não interpretar o que estiver dentro da string – String exp = @“\d” ou String exp = “\\d”.
Exemplos de expressões regulares usando as classes do .NET
Agora com o .NET Framework temos uma poderosa implementação de expressões regulares que incorpora as mais populares características de outras implementações de expressão regular, tais como Perl 5.0 e awk.
O namespace System.Text.RegularExpressions é onde está centralizado o suporte a expressões regulares e tem como classe principal a Regex. Essa classe contém os métodos IsMatch (ver Listagem 1), Matches (ver Listagem 2), Split (ver Listagem 3) e Replace (ver Listagem 4) que você pode utilizar com uma expressão regular, tanto para pesquisar, formatar e substituir strings. Quando usada em pesquisas, a classe Regex pode retornar um simples objeto da classe Match ou uma MatchCollection. Além deste namespace, você também pode manipular expressões regulares no ASP.NET com o controle web RegularExpressionValidator. É um excelente componente para validação de dados digitados pelo usuário.
Vejamos algumas expressões para validar tipos de dados que fazem parte do dia a dia de qualquer aplicação, os códigos estão exemplificados através de aplicações console nas listagens 1 a 5. Caso tenha alguma dúvida em alguma expressão, veja o significado do caracter nas tabelas 1 e 2. Como a classe Regex tem alguns métodos estáticos, não é preciso instanciar um objeto antes de utilizar tais métodos, sendo ainda possível criar instâncias da classe Regex (Listagem 5).
Listagem 1. Uso do método IsMatch para verificar se uma string coincide com uma determinada expressão.
using System;
using System.Text.RegularExpressions;
public class Listagem1{
[STAThread]
static void Main(string[] args) {
//Expressão para validar se é um número
//com 5 dígitos
String numCincoDigitos = @"^\d{5}$";
//O formato do cpf é 999.999.999-99.
// \d{3} \. \d{3} \. \d{3} \- \d{2}
//Limita a qtd de dígitos e usa o ponto
//como um literal (contra-barra)
// O ^ e $ indicam que a coincidência
// pode ser no começo ou no fim da linha
String formatoCPF = @"^\d{3}\.\d{3}\.\d{3}\-\d{2}$";
Console.WriteLine("Digite um número com 5 dígitos: ");
String entrada = Console.ReadLine();
if (Regex.IsMatch(entrada, numCincoDigitos)){
Console.WriteLine(entrada + " é válido!");
}else{
Console.WriteLine(entrada + " Inválido!");
}
Console.WriteLine("\n");
Console.WriteLine("Digite CPF no formato válido: ");
//Verifica se o cpf está no formato válido
entrada = Console.ReadLine();
if (Regex.IsMatch(entrada, formatoCPF)){
Console.WriteLine("Cpf " + entrada + " válido!");
}else{
Console.WriteLine("Cpf " + entrada + " inválido!");
}
Console.Read();
}
}
Listagem 2. Lista as coincidências usando o MatchCollection que é retornado pelo método Matches
using System;
using System.Text.RegularExpressions;
public class Listagem2{
[STAThread]
static void Main(string[] args){
//Frase com espaço em branco
String frase = "Faça, agora mesmo, a assinatura da sua revista MSDN";
//Qualquer espaço não branco seguido por
//espaço em branco
String exp = @"(\S+)\s";
//Pega as coincidências usando um MatchCollection e lista
// as palavras que vem seguida de espaço em branco
foreach (Match encontrada in Regex.Matches(frase, exp)){
Console.WriteLine("Palavra: " +
encontrada.Value);
}
//OBS: a string MSDN não será listada porque
//não é seguida de espaço em branco.
//Mude a expressão de forma que ela aceite a
//última palavra da frase.
Console.Read();
}
}
Listagem 3. Use o método Split para dividir a string nos pontos onde coincide com a expressão
using System;
using System.Text.RegularExpressions;
public class Listagem3{
[STAThread]
static void Main(string[] args){
//Frase com espaço em branco
String frase = "Faça, agora mesmo, a assinatura da sua revista MSDN";
//O método Split divide a string onde
//coincidir com a expressão
String exp = " |, |,";
//O Split retorna um array de strings
foreach (String substring in Regex.Split(frase, exp)){
Console.WriteLine("Palavra: " + substring);
}
Console.Read();
}
}
Listagem 4. Usa o método Replace para substituir caracteres que coincidem com a expressão
using System;
using System.Text.RegularExpressions;
public class Listagem4{
[STAThread]
static void Main(string[] args){
//A expressão "^\w]" não aceita
//caracteres inválidos
Console.Write("Digite uma frase: ");
Console.WriteLine("Resultado: " +
Regex.Replace(Console.ReadLine(), @"[^\w]", ""));
Console.Read();
}
}
Listagem 5. Cria instâncias da classe Regex
using System;
using System.Text.RegularExpressions;
public class Listagem5{
[STAThread]
static void Main(string[] args){
//Valida email
Regex regExEmail = new Regex(@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
//Valida uma hora no formato HH:MM
Regex regExHora =
new Regex("^([0-1][0-9]|2[0-3]):[0-5][0-9]$");
//Valida se a string possui apóstrofo
Regex regExAspa = new Regex("^[^']*$");
//Valida o tamanho da string até 16 caracteres
Regex regExTamanhoString =
new Regex("^(.|\n){0,16}$");
//Valida CEP
Regex regExCEP =
new Regex(@"^\d{5}\.\d{3}$");
//Valida o caminho do arquivo doc, xls ou exe
Regex regExCaminhoArquivo =
new Regex(@"([a-zA-Z]:(\\w+)*\\[a-zA-Z0_9]+)?.[doc|exe|xls]");
//O email renato#kbca@msn.com é inválido
Console.WriteLine("E-mail renatokbca@msn.com: " +
regExEmail.IsMatch("renatokbca@msn.com"));
Console.WriteLine("CEP 51340-221: " +
regExCEP.IsMatch("51340.221"));
Console.WriteLine("Hora 23:59 " +
regExHora.IsMatch("23:59"));
Console.WriteLine("Caminho c:\\msdnmagazine.doc " +
regExCaminhoArquivo.IsMatch("c:\\msdnmagazine.doc"));
Console.Read();
}
}
Conclusão
Com os exemplos mostrados neste artigo você percebeu quão poderosa e flexível é a linguagem. Crie suas expressões e evite perder tempo com código que não seja a regra de negócio. Para seu aprimoramento e desenvolvimento, recomendo pesquisar os outros métodos das classes Regex e no namespace System.Text.RegularExpression.
Referências:
RegexDesigner.NET é uma excelente aplicação windows escrita em C# para validação de expressão regular.
http://www.sellsbrothers.com/tools/#regexd
Site com vários exemplos de expressão regular e opção para validar expressão
OLHO: Trabalhando com expressões regulares você pode fazer em uma linha o que não conseguiria fazer em dezenas de linhas de código.