Por que \\d em regex é mais lento que [0-9]? Análise em C#

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: O mito da velocidade das expressões regulares

\n\n

Se você já escreveu em C# e usou expressões regulares, certamente já enfrentou o dilema: usar \\d ou [0-9] para buscar dígitos? À primeira vista, não há diferença — ambos encontram dígitos. Mas desenvolvedores experientes, que polim o desempenho até o brilho, sabem: \\d pode ser significativamente mais lento que [0-9].

\n\n

No Stack Overflow, essa pergunta acumulou milhares de visualizações e dezenas de comentários. Hoje, não vamos apenas recontar a resposta, mas vamos olhar sob o capô do motor .NET Regex e entender por que isso acontece e como isso afeta seu código. Escreveremos benchmarks reais em C#, analisaremos o código IL e descobriremos quando é hora de se preocupar e quando podemos relaxar.

\n\n

Prontos? Vamos lá!

\n\n

O que são \\d e [0-9] em C#?

\n\n

Para começar — o básico. Na maioria das implementações de expressões regulares (incluindo C#), \\d é um atalho para "qualquer dígito". E [0-9] é uma classe de caracteres que lista explicitamente o intervalo de '0' a '9'.

\n\n

Parece a mesma coisa. Mas o diabo, como sempre, está nos detalhes.

\n\n

Unicode vs ASCII

\n\n

No .NET (a partir do .NET Framework 4.5 em diante), \\d por padrão corresponde a qualquer caractere da categoria Unicode Nd (Number, Decimal Digit). Isso não inclui apenas os dígitos arábicos 0-9, mas também:

\n
    \n
  • Dígitos devanágari (०१२३...)
  • \n
  • Dígitos da escrita tâmil
  • \n
  • Dígitos arábico-orientais
  • \n
  • E dezenas de outros sistemas numéricos
  • \n
\n\n

Já [0-9] é um intervalo rigidamente definido de códigos ASCII (48-57). Apenas 0,1,2,3,4,5,6,7,8,9. Nada de Unicode.

\n\n

Por que a velocidade é diferente? Análise interna

\n\n

Agora o principal: por que \\d é mais lento? Vamos entender no nível do motor.

\n\n

1. Número de verificações

\n\n

Quando o motor encontra [0-9], ele faz uma coisa: verifica se o código do caractere está no intervalo 48-57. Isso é uma comparação (ou duas, considerando os limites).

\n\n

Quando o motor encontra \\d, ele precisa:

\n
    \n
  1. Obter a categoria Unicode do caractere (isso já é mais caro do que apenas um int).
  2. \n
  3. Compará-la com a categoria Nd.
  4. \n
\n\n

A categoria Unicode não é apenas um número, é uma bandeira inteira que é calculada por meio de tabelas internas. Para dígitos latinos, o resultado será o mesmo, mas o caminho até ele é mais longo.

\n\n

2. Cache e JIT

\n\n

No .NET, o Regex é compilado para código intermediário (IL) ou até mesmo nativo (se for usado RegexOptions.Compiled). Para [0-9], o JIT pode gerar uma sequência muito simples de instruções: um par de comparações e um desvio condicional. Para \\d — uma chamada ao método CharUnicodeInfo.GetUnicodeCategory e uma comparação subsequente.

\n\n

Chamar um método tem custos indiretos: push de argumentos, call, verificação de pilha. Mesmo que o método seja pequeno, ele ainda é mais lento que uma comparação embutida.

\n\n

3. Exemplo de um benchmark real

\n\n

Vamos escrever um teste simples em C# usando BenchmarkDotNet (certifique-se de instalá-lo via NuGet se for repetir):

\n\n
using BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Running;\nusing System.Text.RegularExpressions;\n\npublic class RegexBenchmark\n{\n    private static readonly Regex _digitPattern = new Regex(@"\\d", RegexOptions.Compiled);\n    private static readonly Regex _rangePattern = new Regex(@"[0-9]", RegexOptions.Compiled);\n    \n    private const string TestString = "Hello123World456!";\n\n    [Benchmark]\n    public bool DigitPattern() => _digitPattern.IsMatch(TestString);\n\n    [Benchmark]\n    public bool RangePattern() => _rangePattern.IsMatch(TestString);\n}\n\nclass Program\n{\n    static void Main() => BenchmarkRunner.Run<RegexBenchmark>();\n}
\n\n

Resultados (no .NET

Blogs

Book Recommendations