Post

Comparação String Match, Pattern Compile e Validação Manual

Comparação String Match, Pattern Compile e Validação Manual

No dia a dia a conveniência muitas vezes atropela a performance. Um dos exemplos mais comuns é montar uma expressão no String.matches() e nunca mais mexer depois que fez funcionar. Essa abordagem pode se tornar um gargalo crítico em sistemas de alto rendimento, especialmente quando lidamos com grandes volumes de dados.

Neste post, vamos analisar quatro abordagens para validação de CNPJs (incluindo cálculo de dígitos verificadores e suporte a caracteres alfanuméricos): o uso String.matches, Pattern.compile, validação manual sem expressões regulares e uma versão otimizada da validação manual.

Recompilação em toda execução

Quando utilizamos String.matches(regex), a JVM não executa a busca imediatamente. Por baixo dos panos, o Java é obrigado a compilar a String da expressão regular em uma estrutura de dados complexa — uma máquina de estados finitos (NFA - Nondeterministic Finite Automaton) — antes de aplicá-la ao texto.

E ao usar esses métodos utilitários da classe String, a compilação ocorre todas as vezes que o método é invocado. Em um loop de processamento massivo, você está pagando o preço de “reaprender” a mesma regra repetidamente, gerando objetos temporários e desperdiçando ciclos de CPU.

Fluxo de Processamento e Decisões

Para entender o ganho de performance, precisamos visualizar o que acontece quando dissociamos a compilação da execução ou quando removemos o motor de regex completamente em favor de uma lógica imperativa especializada.

Comparaçõesfluxo Comparação dos fluxos das operações.

Implementações em Detalhe

Para os testes, implementamos a validação completa de CNPJs, que agora permitem letras em sua composição. A lógica envolve a remoção de formatação, validação de estrutura e cálculo dos dois dígitos verificadores.

Os algoritmos e regras de validação para o novo formato de CNPJ (alfanumérico) utilizados nos exemplos abaixo foram baseados na documentação técnica oficial disponibilizada pela Receita Federal neste link.

1. Sem Compilar

Aqui, cada chamada a matches e replaceAll dispara uma nova compilação de regex internamente. É o padrão “confortável” que destrói a performance em escala.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.stream.Stream;

class SemCompilar {
  private static final int TAMANHO_CNPJ_SEM_DV = 12;
  private static final String REGEX_CARACTERES_FORMATACAO = "[./-]";
  private static final String REGEX_FORMACAO_BASE_CNPJ = "[A-Z\\d]{12}";
  private static final String REGEX_FORMACAO_DV = "[\\d]{2}";
  private static final String REGEX_VALOR_ZERADO = "^[0]+$";
  private static final int VALOR_BASE = (int) '0';
  private static final int[] PESOS_DV = { 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 };

  public static boolean isValid(String cnpj) {
    if (cnpj != null) {
      cnpj = removeCaracteresFormatacao(cnpj);
      if (isCnpjFormacaoValidaComDV(cnpj)) {
        String dvInformado = cnpj.substring(TAMANHO_CNPJ_SEM_DV);
        String dvCalculado = calculaDV(cnpj.substring(0, TAMANHO_CNPJ_SEM_DV));
        return dvCalculado.equals(dvInformado);
      }
    }
    return false;
  }

  private static String calculaDV(String baseCnpj) {
    if (baseCnpj != null) {
      baseCnpj = removeCaracteresFormatacao(baseCnpj);
      if (isCnpjFormacaoValidaSemDV(baseCnpj)) {
        String dv1 = String.format("%d", calculaDigito(baseCnpj));
        String dv2 = String.format("%d", calculaDigito(baseCnpj.concat(dv1)));
        return dv1.concat(dv2);
      }
    }
    throw new IllegalArgumentException(String.format("Cnpj %s não é válido para o cálculo do DV", baseCnpj));
  }

  private static int calculaDigito(String cnpj) {
    int soma = 0;
    for (int indice = cnpj.length() - 1; indice >= 0; indice--) {
      int valorCaracter = (int) cnpj.charAt(indice) - VALOR_BASE;
      soma += valorCaracter * PESOS_DV[PESOS_DV.length - cnpj.length() + indice];
    }
    return soma % 11 < 2 ? 0 : 11 - (soma % 11);
  }

  private static String removeCaracteresFormatacao(String cnpj) {
    return cnpj.trim().replaceAll(REGEX_CARACTERES_FORMATACAO, "");
  }

  private static boolean isCnpjFormacaoValidaSemDV(String cnpj) {
    return cnpj.matches(REGEX_FORMACAO_BASE_CNPJ) &&
        !cnpj.matches(REGEX_VALOR_ZERADO);
  }

  private static boolean isCnpjFormacaoValidaComDV(String cnpj) {
    return cnpj.matches(REGEX_FORMACAO_BASE_CNPJ.concat(REGEX_FORMACAO_DV)) &&
        !cnpj.matches(REGEX_VALOR_ZERADO);
  }

  void main(String[] args) throws IOException {
    var file = args.length > 0 ? args[0] : "data.txt";
    var entries = new HashMap<String, Boolean>();

    long start = System.nanoTime();

    try (Stream<String> stream = Files.lines(Paths.get(file))) {
      stream.forEach(item -> entries.put(item, isValid(item)));
    }

    long end = System.nanoTime();

    IO.println("Tempo de execução: " + (end - start) / 1_000_000.0 + " ms, itens mapeados: " + entries.size());
  }
}

2. Com Pattern.compile

Nesta versão, os padrões são compilados uma única vez e armazenados em constantes static final. O Matcher reutiliza a estrutura já processada.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.regex.Pattern;
import java.util.stream.Stream;

class ComCompilar {
  private static final int TAMANHO_CNPJ_SEM_DV = 12;
  private static final int VALOR_BASE = '0';

  private static final int[] PESOS_DV = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};

  // Patterns pré-compilados
  private static final Pattern PADRAO_BASE = Pattern.compile("^[A-Z\\d]{12}$");
  private static final Pattern PADRAO_COMPLETO = Pattern.compile("^[A-Z\\d]{12}\\d{2}$");
  private static final Pattern PADRAO_ZERADO = Pattern.compile("^0+$");
  private static final Pattern PADRAO_FORMATACAO = Pattern.compile("[./-]");

  public static boolean isValid(String cnpj) {
    if (cnpj == null) return false;

    cnpj = removeCaracteresFormatacao(cnpj);

    if (!isCnpjFormacaoValidaComDV(cnpj)) return false;

    String base = cnpj.substring(0, TAMANHO_CNPJ_SEM_DV);
    String dvInformado = cnpj.substring(TAMANHO_CNPJ_SEM_DV);
    String dvCalculado = calculaDV(base);

    return dvCalculado.equals(dvInformado);
  }

  private static String calculaDV(String baseCnpj) {
    if (baseCnpj == null) {
      throw new IllegalArgumentException("CNPJ base nulo");
    }

    baseCnpj = removeCaracteresFormatacao(baseCnpj);

    if (!isCnpjFormacaoValidaSemDV(baseCnpj)) {
      throw new IllegalArgumentException("CNPJ inválido: " + baseCnpj);
    }

    int dv1 = calculaDigito(baseCnpj);
    int dv2 = calculaDigito(baseCnpj + dv1);

    return "" + dv1 + dv2;
  }

  private static int calculaDigito(String cnpj) {
    int soma = 0;
    int offset = PESOS_DV.length - cnpj.length();

    for (int i = 0; i < cnpj.length(); i++) {
      int valor = cnpj.charAt(i) - VALOR_BASE;
      soma += valor * PESOS_DV[offset + i];
    }

    int resto = soma % 11;
    return (resto < 2) ? 0 : 11 - resto;
  }

  private static String removeCaracteresFormatacao(String cnpj) {
    return PADRAO_FORMATACAO.matcher(cnpj).replaceAll("").trim();
  }

  private static boolean isCnpjFormacaoValidaSemDV(String cnpj) {
    return PADRAO_BASE.matcher(cnpj).matches()
      && !PADRAO_ZERADO.matcher(cnpj).matches();
  }

  private static boolean isCnpjFormacaoValidaComDV(String cnpj) {
    return PADRAO_COMPLETO.matcher(cnpj).matches()
      && !PADRAO_ZERADO.matcher(cnpj).matches();
  }

  public static void main(String[] args) throws IOException {
    var file = args.length > 0 ? args[0] : "data.txt";
    var entries = new HashMap<String, Boolean>();

    long start = System.nanoTime();

    try (Stream<String> stream = Files.lines(Paths.get(file))) {
      stream.forEach(item -> entries.put(item, isValid(item)));
    }

    long end = System.nanoTime();

    IO.println("Tempo de execução: " + (end - start) / 1_000_000.0 + " ms, itens mapeados: " + entries.size());
  }
}

3. Sem Regex

Aqui abandonamos completamente o motor genérico de expressões regulares. Utilizamos loops simples, StringBuilder e verificações de caracteres individuais.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.stream.Stream;

class SemRegex {
  private static final int TAMANHO_CNPJ_SEM_DV = 12;
  private static final int VALOR_BASE = '0';
  private static final int[] PESOS_DV = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};

  public static boolean isValid(String cnpj) {
    if (cnpj == null) return false;

    cnpj = removeCaracteresFormatacao(cnpj);

    if (!isCnpjFormacaoValidaComDV(cnpj)) return false;

    String base = cnpj.substring(0, TAMANHO_CNPJ_SEM_DV);
    String dvInformado = cnpj.substring(TAMANHO_CNPJ_SEM_DV);
    String dvCalculado = calculaDV(base);

    return dvCalculado.equals(dvInformado);
  }

  private static String calculaDV(String baseCnpj) {
    if (baseCnpj == null) {
      throw new IllegalArgumentException("CNPJ base nulo");
    }

    baseCnpj = removeCaracteresFormatacao(baseCnpj);

    if (!isCnpjFormacaoValidaSemDV(baseCnpj)) {
      throw new IllegalArgumentException("CNPJ inválido para cálculo do DV: " + baseCnpj);
    }

    int dv1 = calculaDigito(baseCnpj);
    int dv2 = calculaDigito(baseCnpj + dv1);

    return "" + dv1 + dv2;
  }

  private static int calculaDigito(String cnpj) {
    int soma = 0;
    int offset = PESOS_DV.length - cnpj.length();

    for (int i = 0; i < cnpj.length(); i++) {
      int valor = cnpj.charAt(i) - VALOR_BASE;
      soma += valor * PESOS_DV[offset + i];
    }

    int resto = soma % 11;
    return (resto < 2) ? 0 : 11 - resto;
  }

  private static String removeCaracteresFormatacao(String cnpj) {
    StringBuilder sb = new StringBuilder(cnpj.length());

    for (int i = 0; i < cnpj.length(); i++) {
      char c = cnpj.charAt(i);
      if (c != '.' && c != '/' && c != '-') {
        sb.append(c);
      }
    }

    return sb.toString().trim();
  }

  private static boolean isCnpjFormacaoValidaSemDV(String cnpj) {
    if (cnpj.length() != TAMANHO_CNPJ_SEM_DV) return false;

    boolean allZero = true;

    for (int i = 0; i < cnpj.length(); i++) {
      char c = cnpj.charAt(i);

      if (!isAlphaNumeric(c)) return false;
      if (c != '0') allZero = false;
    }

    return !allZero;
  }

  private static boolean isCnpjFormacaoValidaComDV(String cnpj) {
    if (cnpj.length() != 14) return false;

    boolean allZero = true;

    for (int i = 0; i < 12; i++) {
      char c = cnpj.charAt(i);

      if (!isAlphaNumeric(c)) return false;
      if (c != '0') allZero = false;
    }

    // últimos 2 devem ser dígitos
    for (int i = 12; i < 14; i++) {
      char c = cnpj.charAt(i);

      if (!isDigit(c)) return false;
      if (c != '0') allZero = false;
    }

    return !allZero;
  }

  private static boolean isDigit(char c) {
    return c >= '0' && c <= '9';
  }

  private static boolean isAlphaNumeric(char c) {
    return (c >= '0' && c <= '9') ||
      (c >= 'A' && c <= 'Z');
  }

  void main(String[] args) throws IOException {
    var file = args.length > 0 ? args[0] : "data.txt";
    var entries = new HashMap<String, Boolean>();

    long start = System.nanoTime();

    try (Stream<String> stream = Files.lines(Paths.get(file))) {
      stream.forEach(item -> entries.put(item, isValid(item)));
    }

    long end = System.nanoTime();

    IO.println("Tempo de execução: " + (end - start) / 1_000_000.0 + " ms, itens mapeados: " + entries.size());
  }
}

4. Sem Regex (Otimizado)

Esta versão tenta levar a otimização manual ao extremo. Eliminamos a criação de objetos intermediários (como String e StringBuilder), utilizando arrays de caracteres fixos, desenrolamos loops (loop unrolling) para o cálculo dos DVs e fundimos a extração de caracteres com a validação básica.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.stream.Stream;

public class SemRegexOtimizado {

    private static final int TAMANHO_CNPJ = 14;

    public static boolean isValid(String input) {
        if (input == null) return false;

        char[] c = new char[TAMANHO_CNPJ];
        int j = 0;

        // extração + validação + early exit
        for (int i = 0; i < input.length() && j < TAMANHO_CNPJ; i++) {
            char ch = input.charAt(i);

            if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z')) {
                c[j++] = ch;
            }
        }

        if (j != TAMANHO_CNPJ) return false;

        // todos zeros check inline (sem loop separado)
        boolean allZero = true;
        for (int i = 0; i < TAMANHO_CNPJ; i++) {
            if (c[i] != '0') {
                allZero = false;
                break;
            }
        }
        if (allZero) return false;

        // cálculo direto DV1 (loop unrolled)
        int s =
            (c[0]-'0') * 5 +
            (c[1]-'0') * 4 +
            (c[2]-'0') * 3 +
            (c[3]-'0') * 2 +
            (c[4]-'0') * 9 +
            (c[5]-'0') * 8 +
            (c[6]-'0') * 7 +
            (c[7]-'0') * 6 +
            (c[8]-'0') * 5 +
            (c[9]-'0') * 4 +
            (c[10]-'0') * 3 +
            (c[11]-'0') * 2;

        int r = s - 11 * (s / 11);
        int dv1 = (r < 2) ? 0 : 11 - r;

        // cálculo direto DV2
        s =
            (c[0]-'0') * 6 +
            (c[1]-'0') * 5 +
            (c[2]-'0') * 4 +
            (c[3]-'0') * 3 +
            (c[4]-'0') * 2 +
            (c[5]-'0') * 9 +
            (c[6]-'0') * 8 +
            (c[7]-'0') * 7 +
            (c[8]-'0') * 6 +
            (c[9]-'0') * 5 +
            (c[10]-'0') * 4 +
            (c[11]-'0') * 3 +
            dv1 * 2;

        r = s - 11 * (s / 11);
        int dv2 = (r < 2) ? 0 : 11 - r;

        return dv1 == (c[12] - '0') &&
               dv2 == (c[13] - '0');
    }

    void main(String[] args) throws IOException {
        var file = args.length > 0 ? args[0] : "data.txt";
        var entries = new HashMap<String, Boolean>();

        long start = System.nanoTime();

        try (Stream<String> stream = Files.lines(Paths.get(file))) {
          stream.forEach(item -> entries.put(item, isValid(item)));
        }

        long end = System.nanoTime();

        IO.println("Tempo de execução: " + (end - start) / 1_000_000.0 + " ms, itens mapeados: " + entries.size());
    }
}

Comparação de Resultados

Abaixo, os resultados detalhados obtidos após processar um arquivo com 1.000.000 de registros em cada modalidade em 100 execuções independentes.

Tempo de Execução Comparação do tempo total de execução em segundos.

Uso de Memória Impacto no pico de consumo de memória (RSS).

Abaixo, os 10 primeiros de cada resultado obtido.

EstratégiaExecuçãoTempo Total (s)Tempo Java (ms)Memória (RSS MB)
matches com String#15.74s4512.91ms1167.35 MB
matches com String#25.97s4290.21ms1549.72 MB
matches com String#34.45s3273.82ms1210.53 MB
matches com String#44.59s3319.22ms1265.25 MB
matches com String#54.63s3461.82ms1411.28 MB
matches com String#64.77s3643.85ms1308.84 MB
matches com String#74.53s3311.74ms1204.71 MB
matches com String#84.54s3233.60ms1211.24 MB
matches com String#94.45s3295.32ms1338.41 MB
matches com String#104.55s3371.40ms1352.38 MB
EstratégiaExecuçãoTempo Total (s)Tempo Java (ms)Memória (RSS MB)
Pattern.compile#13.39s1748.88ms500.88 MB
Pattern.compile#23.09s1905.52ms454.08 MB
Pattern.compile#32.84s1669.36ms462.46 MB
Pattern.compile#42.77s1554.83ms469.36 MB
Pattern.compile#52.69s1587.82ms451.07 MB
Pattern.compile#62.67s1606.12ms454.46 MB
Pattern.compile#72.98s1878.03ms434.51 MB
Pattern.compile#82.86s1597.64ms402.27 MB
Pattern.compile#92.64s1588.42ms433.07 MB
Pattern.compile#102.68s1600.39ms447.91 MB
EstratégiaExecuçãoTempo Total (s)Tempo Java (ms)Memória (RSS MB)
Sem Regex#12.47s926.41ms328.81 MB
Sem Regex#22.68s1324.74ms336.83 MB
Sem Regex#31.95s854.19ms335.47 MB
Sem Regex#42.16s1001.36ms348.33 MB
Sem Regex#52.30s1120.37ms312.73 MB
Sem Regex#62.11s872.31ms369.33 MB
Sem Regex#71.91s812.20ms311.07 MB
Sem Regex#82.09s953.61ms382.05 MB
Sem Regex#92.18s1081.28ms337.66 MB
Sem Regex#102.27s954.62ms359.85 MB
EstratégiaExecuçãoTempo Total (s)Tempo Java (ms)Memória (RSS MB)
Sem Regex Otimizado#11.93s707.63ms280.72 MB
Sem Regex Otimizado#22.34s532.44ms253.92 MB
Sem Regex Otimizado#31.82s658.33ms319.54 MB
Sem Regex Otimizado#41.75s510.32ms231.54 MB
Sem Regex Otimizado#51.79s684.61ms322.10 MB
Sem Regex Otimizado#61.66s570.10ms289.38 MB
Sem Regex Otimizado#71.70s590.80ms323.61 MB
Sem Regex Otimizado#81.81s580.85ms232.74 MB
Sem Regex Otimizado#91.74s534.97ms268.29 MB
Sem Regex Otimizado#101.64s540.97ms302.96 MB

Disclaimer: Estes resultados presentes nas imagens foram obtidos utilizando o comando /usr/bin/time -v. Esses valores incluem o tempo total de carregamento da JVM, o carregamento de todas as classes, a leitura do arquivo em disco e a saída no console. Em sistemas de longa duração (como apis rest), a diferença real pode ser ainda mais drástica, pois o overhead inicial da JVM é diluído ao longo do tempo.

Tempos só do loop

Resultado obtido com (end - start) / 1_000_000.0 envolta do try

Tempo de Execução Comparação do tempo total de execução em segundos só do loop.

Enquanto as tabela acima mostra o custo que o sistema operacional percebe, a tabela abaixo isola apenas o tempo gasto dentro do loop de processamento do Java, descartando o tempo de inicialização da JVM.

ExecuçãoSem Compilar (ms)Com Compilar (ms)Sem Regex (ms)Sem Regex Otimizado (ms)
#14512.91 ms1748.88 ms926.41 ms707.63 ms
#24290.21 ms1905.52 ms1324.74 ms532.44 ms
#33273.82 ms1669.36 ms854.19 ms658.33 ms
#43319.22 ms1554.83 ms1001.36 ms510.32 ms
#53461.82 ms1587.82 ms1120.37 ms684.61 ms
#63643.85 ms1606.12 ms872.31 ms570.10 ms
#73311.74 ms1878.03 ms812.20 ms590.80 ms
#83233.60 ms1597.64 ms953.61 ms580.85 ms
#93295.32 ms1588.42 ms1081.28 ms534.97 ms
#103371.40 ms1600.39 ms954.62 ms540.97 ms

Ambiente de Teste

Para garantir a reprodutibilidade dos resultados, abaixo estão os detalhes do ambiente onde os benchmarks foram executados:

1
2
3
4
5
OS: Ubuntu 24.04.3 LTS x86_64  
Kernel: 6.17.0-20-generic 
Shell: bash 5.2.21 
CPU: Intel i5-7200U (4) @ 2.500GHz 
Memory: 19876MiB

Versão do Java:

1
2
3
openjdk version "25.0.2" 2026-01-20
OpenJDK Runtime Environment (build 25.0.2+10-Ubuntu-124.04)
OpenJDK 64-Bit Server VM (build 25.0.2+10-Ubuntu-124.04, mixed mode, sharing)

Análise dos Dados

  • Sem Compilar: A abordagem mais custosa. O alto uso de memória reflete a criação e descarte frenético de objetos de regex e matchers. O tempo de execução sofre com o desperdício de ciclos em “re-analisar” a mesma regra um milhão de vezes.
  • Compilado: Uma melhoria substancial. Ao pré-compilar os Patterns, eliminamos o overhead de análise sintática do regex em cada iteração. O consumo de memória estabiliza, mas ainda pagamos o preço de percorrer a NFA do motor de regex.
  • Sem Regex: Ao implementar uma lógica manual, removemos toda a abstração do motor de regex. O Java consegue otimizar loops simples de forma extremamente eficiente via JIT compiler, resultando no menor tempo e menor pegada de memória.
  • Sem Regex Otimizado: Ao removermos a utilização de StringBuilder e substring, e utilizarmos loop unrolling, permitimos que o JIT realize mais otimizações e o controle fino sobre a alocação de memória e o fluxo de execução faz toda a diferença.

Por que a Lógica Manual Vence?

Para uma validação simples como “verificar se os caracteres são alfanuméricos”, o motor de regex é um exagero, é como usar uma bazuca para matar uma formiga.

Ao escrever isAlphaNumeric(char c), você está dando uma instrução direta ao processador. Não há análise de grafos, não há decisões de backtracking. É pura comparação de bits, o que torna o processamento previsível e veloz.

Dicas finais

  1. Se você vai usar uma regex mais de uma vez, compile-a em uma constante.
  2. Validações Críticas: Se o seu código está dentro de um loop que processa milhões de registros (ETLs, processamento de logs, gateways de pagamento), tente montar uma pipeline sem o regex pode te trazer muitos benefícios.
  3. Legibilidade vs Performance: Use regex para regras complexas onde o código manual se tornaria ilegível ou propenso a erros. O ganho de performance só justifica a complexidade extra se houver volume de dados suficiente.
This post is licensed under CC BY 4.0 by the author.