Menu

Regex en C# : Match, Replace, Split et groupes avec des exemples

Comment utiliser les expressions régulières en C# avec System.Text.RegularExpressions : IsMatch, Match et Matches, groupes numérotés et nommés, Replace avec substitutions et lambda, Split, RegexOptions, chaînes verbatim pour les motifs, validation de saisie, délais d'expiration, et l'attribut GeneratedRegex de .NET 7.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Une expression régulière (regex) est un motif qui décrit du texte : « quatre chiffres, un tiret, deux chiffres », « un mot qui commence par une majuscule », « tout ce qui est entre crochets ». En C#, la classe Regex de System.Text.RegularExpressions trouve, extrait, remplace et découpe du texte avec de tels motifs.

IsMatch, Match et Matches

Trois méthodes couvrent la plupart des recherches. IsMatch répond oui ou non, Match renvoie la première correspondance, et Matches les renvoie toutes :

Sortie :

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

Un Match échoué ne renvoie pas null : il renvoie un Match dont Success vaut false et dont Value est vide. Testez toujours Success avant d'utiliser une correspondance.

La recherche des quatre chiffres montre comment les motifs attrapent plus que prévu. \b\d{4}\b signifie « exactement quatre chiffres formant un mot entier », et les années contenues dans les dates remplissent ce critère, car - est une limite de mot. Resserrez le motif pour dire ce que vous voulez dire, par exemple (?<=Order |order )\d+, ou utilisez des groupes, présentés ensuite.

Des chaînes verbatim pour les motifs

La syntaxe des regex utilise des barres obliques inverses partout (\d, \s, \b), tout comme les séquences d'échappement des chaînes C#. Dans une chaîne normale, "\d" ne compile même pas (CS1009, unrecognized escape sequence) et "\b" se compile en caractère de retour arrière, ce qui modifie silencieusement le motif. Écrivez les motifs sous forme de chaînes verbatim avec @, où une barre oblique inverse n'est qu'une barre oblique inverse :

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

Dans une chaîne verbatim, un guillemet double s'écrit "". Les littéraux de chaîne bruts de C# 11 ("""...""") évitent même cela.

Groupes : extraire des parties d'une correspondance

Les parenthèses créent un groupe, et le texte de chaque groupe est disponible après la correspondance. Les groupes sont numérotés à partir de 1 dans l'ordre de leur parenthèse ouvrante ; le groupe 0 est la correspondance entière. Les groupes nommés, (?<name>...), sont plus faciles à lire et survivent aux modifications du motif :

Sortie :

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

[ et ] ont un sens en regex (une classe de caractères), donc faire correspondre les crochets littéraux demande \[ et \]. Il en va de même pour . * + ? ( ) { } ^ $ | \. Pour faire correspondre littéralement une chaîne fournie par l'utilisateur dans un motif, passez-la par Regex.Escape, qui échappe les métacaractères pour vous : Regex.Escape("price (USD)") renvoie price\ \(USD\) (les espaces sont aussi échappés, ce qui est sans conséquence).

Les parenthèses qui ne font que grouper, sans capturer, s'écrivent (?:...). Elles gardent la numérotation des groupes propre et sont légèrement plus rapides.

Replace : substitutions et lambdas

Regex.Replace remplace chaque correspondance. Dans la chaîne de remplacement, $1 insère le groupe 1, ${name} un groupe nommé et $0 la correspondance entière. Pour tout ce qu'un modèle ne peut pas exprimer, passez une fonction (en général une lambda) qui reçoit chaque Match et renvoie son remplacement :

Sortie :

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

Le masquage de carte utilise une assertion avant (lookahead), (?=...) : elle ne fait correspondre un chiffre que si quatre autres chiffres le suivent, sans les consommer. Les assertions ((?=...), (?!...), (?<=...), (?<!...)) testent le contexte sans l'inclure dans la correspondance, ce qui permet à un seul Replace de garder intacts les quatre derniers chiffres.

Pour du texte simple sans motif, string.Replace est plus simple et plus rapide ; tournez-vous vers Regex.Replace quand ce qu'il faut remplacer varie.

Split

Regex.Split découpe à chaque correspondance d'un motif, ce qui gère des séparateurs variables :

Sortie :

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

Si le motif contient des groupes de capture, Regex.Split inclut les séparateurs capturés dans le résultat ; utilisez (?:...) quand vous n'en voulez pas.

RegexOptions

Les options changent le comportement de tout le motif. Combinez-les avec | :

Sortie :

1
2
error INFO Error
True

Celles que vous utiliserez :

  • IgnoreCase : correspondance insensible à la casse (forme en ligne (?i)).
  • Multiline : ^ et $ correspondent au début et à la fin de chaque ligne, pas seulement de la chaîne entière.
  • Singleline : . correspond aussi à \n (par défaut, il correspond à tout caractère sauf un saut de ligne).
  • IgnorePatternWhitespace : les espaces du motif sont ignorés et # commence un commentaire, pour les longs motifs écrits sur plusieurs lignes.
  • CultureInvariant : avec IgnoreCase, compare sans les règles de la culture courante.
  • Compiled : compile le motif en IL une fois, pour une regex utilisée de nombreuses fois.

Valider une saisie : ancres et la question de l'e-mail

Pour valider, ancrez le motif avec ^ et $, sinon il accepte dès qu'une partie de l'entrée correspond :

Sortie :

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

Le motif d'e-mail ne vérifie que la forme : quelque chose sans espace ni @, un seul @, un domaine avec un point. C'est voulu. La vraie grammaire des adresses (RFC 5322) autorise des parties locales entre guillemets, des commentaires et des domaines en adresse IP littérale ; les motifs qui essaient de la couvrir font des centaines de caractères et rejettent encore des adresses valides que les gens utilisent vraiment. Et une adresse syntaxiquement parfaite peut quand même être rejetée. Vérifiez la forme, puis envoyez un e-mail de confirmation. System.Net.Mail.MailAddress offre une autre vérification de forme si vous préférez ne pas écrire de motif.

Performances : méthodes statiques, instances, compilation

Les méthodes statiques (Regex.IsMatch(input, pattern)) analysent le motif et le gardent dans un petit cache (15 motifs par défaut), donc des appels répétés avec le même motif coûtent peu. Pour un motif utilisé dans une boucle critique, créez une instance de Regex et gardez-la dans un champ static readonly ; ajoutez RegexOptions.Compiled si elle s'exécute des milliers de fois, en échangeant un démarrage plus lent contre une correspondance plus rapide.

Délais d'expiration et retour arrière catastrophique

Certains motifs prennent un temps exponentiel sur certaines entrées. Le cas classique est un quantificateur imbriqué comme ^(a+)+$ face à "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" : le moteur essaie toutes les façons de répartir les a entre les deux + avant d'abandonner. Sur un serveur web, une seule entrée de ce genre venant d'un utilisateur peut monopoliser un cœur de processeur (une attaque « ReDoS »). Donnez un délai d'expiration à toute regex qui traite des entrées non fiables :

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

Chaque méthode statique a aussi une surcharge qui prend un délai. .NET 7 a également ajouté RegexOptions.NonBacktracking, un moteur qui garantit un temps linéaire au prix de certaines fonctionnalités (assertions, références arrière).

GeneratedRegex (.NET 7)

.NET 7 a ajouté un générateur de source qui transforme un motif en code C# ordinaire à la compilation. Vous obtenez la vitesse de Compiled sans coût au démarrage, le code généré est lisible dans l'IDE, et il fonctionne avec le trimming et 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

Sur .NET 7 et plus, c'est la forme recommandée pour tout motif connu à la compilation.

Erreurs courantes

  • Des ancres manquantes dans la validation. Sans ^...$, IsMatch accepte une entrée qui contient simplement une correspondance.
  • Des chaînes normales pour les motifs. "\b" est un retour arrière, pas une limite de mot. Utilisez @"...".
  • Des caractères spéciaux non échappés. . correspond à n'importe quel caractère ; 3.50 comme motif correspond aussi à 3x50. Échappez avec \., ou avec Regex.Escape pour une saisie utilisateur.
  • Des quantificateurs gourmands. <.*> sur <b>bold</b> correspond à toute la chaîne. Utilisez la version paresseuse .*? ou une classe négative [^>]*.
  • Pas de délai d'expiration sur une entrée non fiable. Un quantificateur imbriqué peut bloquer une requête.
  • Des regex pour des formats structurés. Le HTML, le JSON et le CSV avec guillemets demandent un analyseur, pas un motif.

Questions fréquentes

Comment utiliser une regex en C# ?

Ajoutez using System.Text.RegularExpressions; et appelez les méthodes statiques de Regex : Regex.IsMatch(input, pattern) renvoie un bool, Regex.Match renvoie la première correspondance, Regex.Matches toutes, Regex.Replace remplace et Regex.Split découpe. Écrivez les motifs sous forme de chaînes verbatim, @"\d+", pour que les barres obliques inverses arrivent intactes au moteur de regex.

Comment obtenir la valeur d'un groupe d'une correspondance regex en C# ?

Entourez de parenthèses la partie voulue et lisez match.Groups[1].Value (les groupes sont numérotés à partir de 1 ; le groupe 0 est la correspondance entière). Les groupes nommés sont plus clairs : (?<year>\d{4}) se lit avec match.Groups["year"].Value. Vérifiez d'abord match.Success, car une correspondance échouée a des groupes vides plutôt que null.

Comment remplacer du texte avec une regex en C# ?

Regex.Replace(input, pattern, replacement) remplace chaque correspondance. Le remplacement peut faire référence aux groupes : $1 pour un groupe numéroté, ${name} pour un groupe nommé, $0 pour la correspondance entière. Pour une logique qu'un modèle ne peut pas exprimer, passez une lambda : Regex.Replace(text, @"\d+", m => (int.Parse(m.Value) * 2).ToString()).

Comment rendre une regex C# insensible à la casse ?

Passez RegexOptions.IgnoreCase : Regex.IsMatch(input, "error", RegexOptions.IgnoreCase). Les options se combinent avec |, par exemple RegexOptions.IgnoreCase | RegexOptions.Multiline. Vous pouvez aussi l'activer dans le motif avec (?i).

Comment valider une adresse e-mail avec une regex en C# ?

Un motif comme ^[^@\s]+@[^@\s]+\.[^@\s]+$ détecte les fautes évidentes (@ manquant, espaces, pas de point dans le domaine) et c'est en général tout ce dont un formulaire a besoin. Une regex ne peut pas vraiment valider une adresse : la grammaire complète autorise des formes qu'aucun motif pratique ne gère, et une adresse syntaxiquement valide peut ne pas exister. Vérifiez la forme de base, puis confirmez en envoyant un e-mail de vérification.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER