2010-04-16 7 views
9

Je souhaite utiliser l'expression régulière de la même manière que la chaîne.Format. Je vais vous expliquerRemplacer le groupe nommé dans la regex par la valeur

J'ai:

string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$"; 
string input = "abc_123_def"; 
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); 
string replacement = "456"; 
Console.WriteLine(regex.Replace(input, string.Format("${{PREFIX}}{0}${{POSTFIX}}", replacement))); 

Cela fonctionne, mais je dois fournir "entrée" à Regex.Replace. Je ne veux pas ça. Je veux utiliser un motif pour faire correspondre mais aussi pour créer des chaînes de la même manière qu'avec un format de chaîne, en remplaçant le groupe nommé "ID" par une valeur. Est-ce possible?

Je cherche quelque chose comme:

string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$"; 
string result = ReplaceWithFormat(pattern, "ID", 999); 

Résultat contiendra "abc_999_def". Comment accomplir cela?

+0

Ce n'est pas exactement ce que vous cherchez, mais peut être utile. C'est fondamentalement une chaîne nommée.format, au lieu d'indexé. http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx – chilltemp

Répondre

12

Non, il n'est pas possible d'utiliser une expression régulière sans fournir d'entrée. Il doit avoir quelque chose à travailler, le modèle ne peut pas ajouter de données au résultat, tout doit provenir de l'entrée ou du remplacement.

intead d'utiliser String.Format, vous pouvez utiliser un coup d'oeil derrière et un regard vers l'avenir pour spécifier la partie entre « abc_ » et « _def », et de le remplacer:

string result = Regex.Replace(input, @"(?<=abc_)\d+(?=_def)", "999"); 
+9

Pourquoi le downvote? Si vous n'expliquez pas ce que vous pensez être faux, cela ne peut pas améliorer la réponse. – Guffa

+1

Voir la réponse et le commentaire de l'utilisateur1817787 –

+0

La meilleure réponse n'est pas ce post mais ce [un] (http://stackoverflow.com/a/13342795/2183236). – fharreau

13

Oui, il est possible :

public static class RegexExtensions 
{ 
    public static string Replace(this string input, Regex regex, string groupName, string replacement) 
    { 
     return regex.Replace(input, m => 
     { 
      return ReplaceNamedGroup(input, groupName, replacement, m); 
     }); 
    } 

    private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m) 
    { 
     string capture = m.Value; 
     capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length); 
     capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement); 
     return capture; 
    } 
} 

Utilisation:

Regex regex = new Regex("^(?<PREFIX>abc_)(?<ID>[0-9]+)(?<POSTFIX>_def)$"); 

string oldValue = "abc_123_def"; 
var result = oldValue.Replace(regex, "ID", "456"); 

Le résultat est: abc_456_def

+2

-1. 'fu Guffa' est hautement inapproprié. –

+1

Ne faites pas vos jimmies si bruissés. – Justin

+0

Non, il n'est pas possible d'utiliser une expression régulière sans fournir d'entrée. Vous fournissez une entrée dans le code dans votre réponse, donc cela ne démontre pas ce que vous prétendez. – Guffa

3

Il y avait un problème dans la réponse user1817787 et j'ai dû apporter une modification à la fonction ReplaceNamedGroup comme suit.

private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m) 
{ 
    string capture = m.Value; 
    capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length); 
    capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement); 
    return capture; 
} 
+0

il regarde le même code ... – JobaDiniz

+1

@JobaDiniz Il a édité sa réponse pour inclure le mien. Voir https://stackoverflow.com/posts/13342795/revisions – Justin

0

Une autre version modifiée de la méthode originale par @ user1817787, celui-ci prend en charge plusieurs instances du groupe nommé (comprend également similaire à celui fixe @Justin affiché (renvoie le résultat en utilisant {match.Index, match de .length} au lieu de {0, input.Length})):

public static string ReplaceNamedGroup(
    string input, string groupName, string replacement, Match match) 
{ 
    var sb = new StringBuilder(input); 
    var matchStart = match.Index; 
    var matchLength = match.Length; 

    var captures = match.Groups[groupName].Captures.OfType<Capture>() 
     .OrderByDescending(c => c.Index); 

    foreach (var capt in captures) 
    { 
     if (capt == null) 
      continue; 

     matchLength += replacement.Length - capt.Length; 

     sb.Remove(capt.Index, capt.Length); 
     sb.Insert(capt.Index, replacement); 
    } 

    var end = matchStart + matchLength; 
    sb.Remove(end, sb.Length - end); 
    sb.Remove(0, matchStart); 

    return sb.ToString(); 
} 
+0

Si la longueur de remplacement est différente de la longueur de capture, les index seront jetés après le premier retrait/insertion. – user1620220

+0

@ user1620220 J'ai supposé le remplacement de caractères uniques par des caractères uniques comme dans l'exemple de la question 123 ==> 999. Par exemple. pour masquer les données sensibles. –

0

La solution simple est de se référer aux groupes appariés en remplacement. Donc, le préfixe est $1 et Postfix est $3.

J'ai ai pas testé le code ci-dessous, mais devrait fonctionner semblable à un regEx j'ai écrit récemment:

string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$"; 
string input = "abc_123_def"; 
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); 
string replacement = String.Format("$1{0}$3", "456"); 
Console.WriteLine(regex.Replace(input, string.Format("${{PREFIX}}{0}${{POSTFIX}}", replacement))); 
1

Je raccourcies ReplaceNamedGroup, en soutenant plusieurs captures.

private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m) 
{ 
    string result = m.Value; 
    foreach (Capture cap in m.Groups[groupName]?.Captures) 
    { 
    result = result.Remove(cap.Index - m.Index, cap.Length); 
    result = result.Insert(cap.Index - m.Index, replacement); 
    } 
return result; 
} 
-1

Vous devriez consulter la documentation de RegEx remplacer here

J'ai créé ce pour remplacer un groupe nommé. Je ne peux pas utiliser la solution de cette boucle sur tous les noms de groupes car j'ai des cas où toutes les expressions ne sont pas groupées.

public static string ReplaceNamedGroup(this Regex regex, string input, string namedGroup, string value) 
    { 
     var replacement = Regex.Replace(regex.ToString(), 
      @"((?<GroupPrefix>\(\?)\<(?<GroupName>\w*)\>(?<Eval>.[^\)]+)(?<GroupPostfix>\)))", 
      @"${${GroupName}}").TrimStart('^').TrimEnd('$'); 
     replacement = replacement.Replace("${" + namedGroup + "}", value); 
     return Regex.Replace(input, regex.ToString(), replacement); 
    } 
Questions connexes