Menu

Regex em C#: Match, Replace, Split e grupos com exemplos

Como usar expressões regulares em C# com System.Text.RegularExpressions: IsMatch, Match e Matches, grupos numerados e nomeados, Replace com substituições e com uma lambda, Split, RegexOptions, strings verbatim para padrões, validar entradas, timeouts e o atributo GeneratedRegex do .NET 7.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Uma expressão regular (regex) é um padrão que descreve texto: "quatro dígitos, um hífen, dois dígitos", "uma palavra que começa com letra maiúscula", "qualquer coisa entre colchetes". Em C#, a classe Regex, em System.Text.RegularExpressions, encontra, extrai, substitui e divide texto com esses padrões.

IsMatch, Match e Matches

Três métodos cobrem a maioria das buscas. IsMatch responde sim ou não, Match retorna a primeira correspondência e Matches retorna todas elas:

Saída:

True
2026-03-14 at index 22
4 matches:
  1042
  2026
  1043
  2026
Success: False, Value: []

Um Match que falhou não retorna null: retorna um Match cujo Success é false e cujo Value é vazio. Sempre teste o Success antes de usar uma correspondência.

A busca por quatro dígitos mostra como os padrões pegam mais do que se pretendia. \b\d{4}\b significa "exatamente quatro dígitos como uma palavra inteira", e os anos dentro das datas se encaixam, porque - é um limite de palavra. Aperte o padrão para dizer o que você quer, por exemplo (?<=Order |order )\d+, ou use grupos, a seguir.

Strings verbatim para padrões

A sintaxe de regex usa barras invertidas em todo lugar (\d, \s, \b), e os escapes de string de C# também. Em uma string comum, "\d" nem compila (CS1009, unrecognized escape sequence) e "\b" compila como um caractere de backspace, mudando o padrão sem aviso. Escreva os padrões como strings verbatim com @, em que uma barra invertida é só uma barra invertida:

var a = new Regex("\\d+\\.\\d{2}");   // escaped twice: hard to read
var b = new Regex(@"\d+\.\d{2}");     // verbatim: what the regex engine sees

Em uma string verbatim, uma aspa dupla se escreve "". Os raw string literals do C# 11 ("""...""") evitam até isso.

Grupos: extraindo partes de uma correspondência

Parênteses criam um grupo, e o texto de cada grupo fica disponível depois da correspondência. Os grupos são numerados a partir de 1, na ordem dos parênteses de abertura; o grupo 0 é a correspondência inteira. Grupos nomeados, (?<name>...), são mais fáceis de ler e sobrevivem a edições no padrão:

Saída:

2026-03-14
ERROR
payments
Card declined for order 1042
60 by 90, area 5400

[ e ] têm um significado em regex (uma classe de caracteres), então para corresponder aos colchetes literais é preciso \[ e \]. O mesmo vale para . * + ? ( ) { } ^ $ | \. Para corresponder literalmente a uma string fornecida pelo usuário dentro de um padrão, passe-a por Regex.Escape, que escapa os metacaracteres para você: Regex.Escape("price (USD)") retorna price\ \(USD\) (os espaços também são escapados, o que não causa problema).

Parênteses que só agrupam, sem capturar, se escrevem (?:...). Eles mantêm a numeração dos grupos limpa e são um pouco mais rápidos.

Replace: substituições e lambdas

Regex.Replace substitui todas as correspondências. Na string de substituição, $1 insere o grupo 1, ${name} um grupo nomeado e $0 a correspondência inteira. Para qualquer coisa que um modelo não consiga expressar, passe uma função (normalmente uma lambda) que recebe cada Match e retorna a sua substituição:

Saída:

Invoiced 14/03/2026, paid 02/04/2026.
too many spaces
Card **** **** **** 1234
Tea 2.75, Scone 3.52, Jam 1.10

A máscara do cartão usa um lookahead, (?=...): ele corresponde a um dígito só se mais quatro dígitos vierem depois, sem consumi-los. Os lookarounds ((?=...), (?!...), (?<=...), (?<!...)) testam o contexto sem incluí-lo na correspondência, e é isso que permite a um único Replace manter intactos os últimos quatro dígitos.

Para texto simples, sem padrão, string.Replace é mais simples e mais rápido; recorra a Regex.Replace quando o que precisa ser substituído varia.

Split

Regex.Split divide a cada correspondência de um padrão, o que resolve separadores que variam:

Saída:

csharp / dotnet / regex / tutorial / beginner
Order Shipped Event Handler

Se o padrão contiver grupos de captura, Regex.Split inclui os separadores capturados no resultado; use (?:...) quando não quiser isso.

RegexOptions

As opções mudam o comportamento do padrão inteiro. Combine-as com |:

Saída:

1
2
error INFO Error
True

As que você vai usar:

  • IgnoreCase: correspondência sem diferenciar maiúsculas e minúsculas (forma inline (?i)).
  • Multiline: ^ e $ correspondem ao início e ao fim de cada linha, não só da string inteira.
  • Singleline: . também corresponde a \n (por padrão ele corresponde a qualquer caractere, exceto uma quebra de linha).
  • IgnorePatternWhitespace: os espaços no padrão são ignorados e # inicia um comentário, para padrões longos escritos em várias linhas.
  • CultureInvariant: com IgnoreCase, compara sem as regras da cultura atual.
  • Compiled: compila o padrão para IL uma vez, para uma regex usada muitas vezes.

Validando entradas: âncoras e a questão do e-mail

Para validação, ancore o padrão com ^ e $, senão ele passa assim que parte da entrada corresponde:

Saída:

PT-1000    loose=True  strict=True
pt-1000    loose=False strict=False
XPT-1000Y  loose=True  strict=False
PT-10      loose=False strict=False
ana@example.com   True
ana@example       False
ana @example.com  False
ana@@example.com  False

O padrão de e-mail só verifica a forma: algo sem espaços nem @, um @, um domínio com um ponto. Isso é de propósito. A gramática real dos endereços (RFC 5322) permite partes locais entre aspas, comentários e domínios com IP literal; padrões que tentam cobri-la chegam a centenas de caracteres e ainda rejeitam endereços válidos que as pessoas usam de verdade. E um endereço sintaticamente perfeito ainda pode voltar. Verifique a forma e depois envie um e-mail de confirmação. System.Net.Mail.MailAddress oferece outra verificação de forma, se você preferir não escrever um padrão.

Desempenho: métodos static, instâncias, compiled

Os métodos static (Regex.IsMatch(input, pattern)) interpretam o padrão e o guardam em um pequeno cache (15 padrões por padrão), então chamadas repetidas com o mesmo padrão são baratas. Para um padrão usado em um laço crítico, crie uma instância de Regex e guarde-a em um campo static readonly; acrescente RegexOptions.Compiled se ele executar milhares de vezes, trocando uma inicialização mais lenta por correspondências mais rápidas.

Timeouts e backtracking catastrófico

Alguns padrões levam tempo exponencial em certas entradas. O clássico é um quantificador aninhado como ^(a+)+$ contra "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!": o motor tenta todas as formas de dividir os a entre os dois + antes de desistir. Em um servidor web, uma única entrada dessas vinda de um usuário pode travar um núcleo da CPU (um ataque "ReDoS"). Dê um timeout a toda regex que trata entradas não confiáveis:

var pattern = new Regex(@"^(\w+\s?)*$", RegexOptions.None, TimeSpan.FromMilliseconds(100));
try
{
    bool ok = pattern.IsMatch(userInput);
}
catch (RegexMatchTimeoutException)
{
    // treat as invalid input
}

Todo método static também tem uma sobrecarga que recebe um timeout. O .NET 7 também adicionou RegexOptions.NonBacktracking, um motor que garante tempo linear ao custo de alguns recursos (lookarounds, retrorreferências).

GeneratedRegex (.NET 7)

O .NET 7 adicionou um source generator que transforma um padrão em código C# comum em tempo de compilação. Você ganha a velocidade do Compiled sem custo de inicialização, o código gerado pode ser lido na IDE e funciona com trimming e Native AOT:

public static partial class Patterns
{
    [GeneratedRegex(@"^[A-Z]{2}-\d{4}$", RegexOptions.IgnoreCase)]
    public static partial Regex ProductCode();
}

bool ok = Patterns.ProductCode().IsMatch("pt-1000");   // True

No .NET 7 em diante, essa é a forma recomendada para qualquer padrão conhecido em tempo de compilação.

Erros comuns

  • Faltar âncoras na validação. Sem ^...$, IsMatch aceita entradas que só contêm uma correspondência.
  • Strings comuns para os padrões. "\b" é um backspace, não um limite de palavra. Use @"...".
  • Caracteres especiais sem escape. . corresponde a qualquer caractere; 3.50 como padrão também corresponde a 3x50. Escape com \., ou com Regex.Escape para entradas do usuário.
  • Quantificadores gananciosos. <.*> em <b>bold</b> corresponde à string inteira. Use o preguiçoso .*? ou uma classe negada [^>]*.
  • Nenhum timeout em entradas não confiáveis. Um quantificador aninhado pode travar uma requisição.
  • Regex para formatos estruturados. HTML, JSON e CSV com aspas precisam de um parser, não de um padrão.

Perguntas frequentes

Como usar regex em C#?

Adicione using System.Text.RegularExpressions; e chame os métodos static de Regex: Regex.IsMatch(input, pattern) retorna um bool, Regex.Match retorna a primeira correspondência, Regex.Matches todas elas, Regex.Replace substitui e Regex.Split divide. Escreva os padrões como strings verbatim, @"\d+", para que as barras invertidas cheguem ao motor de regex sem mudança.

Como obter o valor de um grupo de uma correspondência regex em C#?

Coloque parênteses em volta da parte que você quer e leia match.Groups[1].Value (os grupos são numerados a partir de 1; o grupo 0 é a correspondência inteira). Grupos nomeados são mais claros: (?<year>\d{4}) é lido com match.Groups["year"].Value. Verifique match.Success antes, porque uma correspondência que falhou tem grupos vazios, e não null.

Como substituir texto com regex em C#?

Regex.Replace(input, pattern, replacement) substitui todas as correspondências. A substituição pode se referir a grupos: $1 para um grupo numerado, ${name} para um nomeado, $0 para a correspondência inteira. Para uma lógica que um modelo não consegue expressar, passe uma lambda: Regex.Replace(text, @"\d+", m => (int.Parse(m.Value) * 2).ToString()).

Como fazer uma regex em C# ignorar maiúsculas e minúsculas?

Passe RegexOptions.IgnoreCase: Regex.IsMatch(input, "error", RegexOptions.IgnoreCase). As opções se combinam com |, por exemplo RegexOptions.IgnoreCase | RegexOptions.Multiline. Você também pode ativá-la dentro do padrão com (?i).

Como validar um endereço de e-mail com regex em C#?

Um padrão como ^[^@\s]+@[^@\s]+\.[^@\s]+$ pega erros de digitação óbvios (falta de @, espaços, domínio sem ponto) e normalmente é tudo de que um formulário precisa. Uma regex não consegue validar de verdade um endereço: a gramática completa permite formas que nenhum padrão prático trata, e um endereço sintaticamente válido pode não existir. Verifique a forma básica e depois confirme enviando um e-mail de verificação.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR