C# -  Expressões regulares : Encontrando uma ocorrência específica

Hoje veremos como usar expressões regulares para localizar uma ocorrência específica em uma string usando o C#.

A plataforma .NET vem com um poderoso mecanismo de expressões regulares que é acessível a qualquer linguagem da plataforma: VB.NET, C#, etc.

Uma expressão regular ou regex para simplificar, é um modelo que descreve uma certa quantidade de texto. A expressão regular mais simples consiste de um único caractere. Ex: a.

Este padrão irá coincidir com a primeira ocorrência do caractere em uma string. Se a string for "Isto é apenas um teste"  a primeira ocorrência será a letra a antes da letra p (de apenas).

As expressões regulares possuem 11 caracteres especiais conhecidos como meta caracteres, são eles:

Para usar qualquer um destes caracteres como um literal em uma expressão regular temos que usar um caractere de escape (\). Assim se você deseja escrever 1+1=2 deverá usar 1 \+1=2  de outra forma o caractere + terá um significado especial.

A classe Regex é a classe mais importante deste grupo e qualquer código para um expressão regular instancia pelo menos um objeto desta classe ( ou usa um dos métodos estáticos da classe Regex). Você instancia este objeto passando o padrão de critério escrito no formato especial da linguagem usada para expressão regulares.

Vamos supor que você deseja localizar uma ocorrência específica de uma correspondência em uma string, como : encontrar a terceira ocorrência de uma palavra ou  a segunda ocorrência de um valor número.

Podemos implementar a solução para este problema podemos subscrever um array retornado por Regex.Matches  que pesquisa em uma cadeia de caracteres de entrada todas as ocorrências de uma expressão regular e retorna todas as correspondências.

Assim para localizar uma ocorrência particular podemos definir um método chamado EncontrarOcorrenciaDe():

        public static Match EncontrarOcorrenciaDe(string fonte, string criterio, int ocorrencia)
        {
            if (ocorrencia < 1)
            {
                throw (new ArgumentException("Não pode ser menor que 1",nameof(ocorrencia)));
            }
            Regex RE = new Regex(criterio, RegexOptions.Multiline);            
            MatchCollection correspondencias = RE.Matches(fonte);
            
            if (ocorrencia >= correspondencias.Count)
            {
                return (null);
            }
            else
            {
                return (correspondencias[ocorrencia]);
            }
        }

A seguir para localizar cada ocorrência de uma correspondência em uma string vamos criar uma lista de correspondência :

        public static List<Match> EncontrarCadaOcorrenciaDe(string fonte, string criterio, int ocorrencia)
        {
            if (ocorrencia < 1)
            {
                throw (new ArgumentException("Não pode ser menor que 1", nameof(ocorrencia)));
            }
            List<Match> ocorrencias = new List<Match>();
            Regex RE = new Regex(criterio, RegexOptions.Multiline);
            MatchCollection correspondencias = RE.Matches(fonte);
            for (int index = (ocorrencia - 1); index < correspondencias.Count; index += ocorrencia)
            {
                ocorrencias.Add(correspondencias[index]);
            }
            return (ocorrencias);
        }

Temos aqui dois métodos semelhantes, mas distintos.

O primeiro método, EncontrarOcorrenciaDe, retorna uma ocorrência particular de uma correspondência de expressão regular. A ocorrência que você deseja encontrar é passada para este método por meio do parâmetro de ocorrência.

Se a ocorrência particular da correspondência não existe - por exemplo, você pede para encontrar a segunda ocorrência, mas existe apenas uma ocorrência - um nulo é retornado deste método. Por causa disso, você deve verificar se o objeto retornado desse método não é nulo antes de usar esse objeto.

Se a ocorrência específica existir, o objeto Match que contém as informações de correspondência para essa ocorrência é retornado.

O segundo método nesta receita,
EncontrarCadaOcorrenciaDe, funciona de forma semelhante ao método EncontrarOcorrenciaDe, exceto que continua a encontrar uma ocorrência particular de uma correspondência de expressão regular até que o final da string seja alcançado.

Por exemplo, se você pedir para encontrar a segunda ocorrência, este método retornará um List<Match> de zero ou mais objetos Match. Os objetos Match corresponderiam à segunda, quarta, sexta e oitava ocorrências de uma correspondência e assim por diante até que o final da string seja alcançado.

Para testar podemos definir o código a seguir no método Main:

       static void Main(string[] args)
        {
            Match resultadoEncontrado = EncontrarOcorrenciaDe("um dois três um dois três um dois três um dois três um dois três um dois três", "dois", 2);

            Console.WriteLine($"{resultadoEncontrado?.ToString()}\t{resultadoEncontrado?.Index}");

            Console.WriteLine();

            List<Match> resultados = EncontrarCadaOcorrenciaDe("um um dois três um dois três um dois três um dois três", "um", 3);

            foreach (Match m in resultados)
                Console.WriteLine($"{m.ToString()}\t{m.Index}");

            Console.ReadLine();
        }

Executando o projeto teremos o resultado abaixo:


Pegue o projeto completo aqui : CShp_Regex1.zip

"E não comuniqueis com as obras infrutuosas das trevas, mas antes condenai-as. Porque o que eles fazem em oculto até dizê-lo é torpe. Mas todas estas coisas se manifestam, sendo condenadas pela luz, porque a luz tudo manifesta."
Efésios 5:11-13

Referências:


José Carlos Macoratti