Encontrar e extrair um número em uma string (em C#, incluindo Span<T>)

Outro dia, estava procurando estratégias para encontrar e extrair números em strings e cheguei a seguinte estratégia, como a mais popular, no Stackoverflow.

 

O código é simples (o que é bom). Entretanto, a Regex não compilada, junto com as alocações repetidas de strings pressionando o GC não parecem performáticas.

Resolvi fazer algumas soluções alternativas para comparação com a solução proposta. Foram elas:

  • Regex compilados e parsing;
  • Busca caractere-por-caractere por números, extração com substring e parsing;
  • Busca caractere-por-caractere por números, extração com span e parsing;

Também resolvi comparar as performances para encontrar, extrair e converter números no início, no meio e no fim de strings.

using System;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

[MemoryDiagnoser]
public class Program
{
    static void Main(string[] args)
    {
        BenchmarkRunner.Run<Program>();
    }


    [Benchmark]
    [Arguments("42, starting with a number.")]
    [Arguments("With a number 42 in the middle.")]
    [Arguments("The secret number is 42.")]
    [Arguments("42")]
    public int ExtractIntUsingRegex(string input)
    {
        var number = Regex.Match(input, @"d+").Value;
        return int.Parse(number);
    }

    
    Regex numberExtractor;
    [GlobalSetup]
    public void GlobalSetup()
    {
        numberExtractor = new Regex(@"d+", RegexOptions.Compiled);
    }

    [Benchmark]
    [Arguments("42, starting with a number.")]
    [Arguments("With a number 42 in the middle.")]
    [Arguments("The secret number is 42.")]
    [Arguments("42")]
    public int ExtractIntUsingCompiledRegex(string input)
    {
        var number = numberExtractor.Match(input).Value;
        return int.Parse(number);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static (int Start, int Length) GetNumberPosition(string s)
    {
        var start = 0;
        for (int i = 0; i < s.Length; i++)
        {
            if (char.IsDigit(s[i]))
            {
                start = i;
                break;
            }
        }

        for (int i = start + 1; i < s.Length; i++)
        {
            if (!char.IsDigit(s[i]))
            {
                return (start, i - start);
            }
        }

        return (start, s.Length - start);
    }

    [Benchmark]
    [Arguments("42, starting with a number.")]
    [Arguments("With a number 42 in the middle.")]
    [Arguments("The secret number is 42.")]
    [Arguments("42")]
    public int ExtractIntUsingSubstring(string input)
    {
        var position = GetNumberPosition(input);
        return int.Parse(input.Substring(position.Start, position.Length));
    }

    [Benchmark]
    [Arguments("42, starting with a number.")]
    [Arguments("With a number 42 in the middle.")]
    [Arguments("The secret number is 42.")]
    [Arguments("42")]
    public int ExtractIntUsingSpan(string input)
    {
        var position = GetNumberPosition(input);
        var numberSpan = input.AsSpan(position.Start, position.Length);
        return int.Parse(numberSpan);
    }
}

Os resultados obtidos foram bem esclarecedores.

Como imaginava, a performance da estratégia popular no Stackoverflow se mostrou como a menos eficiente em todos os cenários testados.  Utilizar uma Regex compilada melhorou a performance consideravelmente.

A solução utilizando Substring teve a melhor performance quando a string possuia apenas o número.

A solução utilizando Span foi consistentemente a mais eficiente em todos os demais cenários (mérito por não fazer alocações e não gerar coletas). Aliás, essa estratégia de “não alocação” é a grande responsável pela melhoria de performance que temos observado em .NET.

Sugestões para tornar esse código mais performático?

Compartilhe este insight:

Comentários

Participe deixando seu comentário sobre este artigo a seguir:

Subscribe
Notify of
guest
0 Comentários
Inline Feedbacks
View all comments

AUTOR

Elemar Júnior
Fundador e CEO da EximiaCo atua como tech trusted advisor ajudando empresas a gerar mais resultados através da tecnologia.

SOLUÇÕES EXIMIACO

ESTRATÉGIA & EXECUÇÃO EM TI

Simplificamos, potencializamos 
aceleramos resultados usando a tecnologia do jeito certo.

COMO PODEMOS LHE AJUDAR?

Vamos marcar uma conversa para que possamos entender melhor sua situação e juntos avaliar de que forma a tecnologia pode trazer mais resultados para o seu negócio.

COMO PODEMOS LHE AJUDAR?

Vamos marcar uma conversa para que possamos entender melhor sua situação e juntos avaliar de que forma a tecnologia pode trazer mais resultados para o seu negócio.

+55 51 3049-7890 |  contato@eximia.co

0
Queremos saber a sua opinião, deixe seu comentáriox
()
x

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

O seu insight foi excluído com sucesso!

O seu insight foi excluído e não está mais disponível.

O seu insight foi salvo com sucesso!

Ele está na fila de espera, aguardando ser revisado para ter sua publicação programada.