Remplacez seulement certains groupes par Regex

191

Supposons que j'ai le regex suivant:

-(\d+)-

et je veux remplacer, en utilisant C #, le groupe 1 (\d+)par AA, pour obtenir:

-AA-

Maintenant, je le remplace en utilisant:

var text = "example-123-example";
var pattern = @"-(\d+)-";
var replaced = Regex.Replace(text, pattern, "-AA-"); 

Mais je n'aime pas vraiment ça, car si je change le modèle pour qu'il corresponde à la _(\d+)_place, je devrai aussi changer la chaîne de remplacement _AA_, et c'est contraire au principe DRY.

Je recherche quelque chose comme:

Gardez le texte correspondant exactement comme il est, mais changez le groupe 1 par this textet le groupe 2 par another text...

Edit:
Ce n'était qu'un exemple. Je cherche juste une manière générique de faire ce que j'ai dit ci-dessus.

Cela devrait fonctionner pour:

anything(\d+)more_text et n'importe quel modèle que vous pouvez imaginer.

Tout ce que je veux faire, c'est remplacer uniquement les groupes et conserver le reste du match.

Oscar Mederos
la source

Réponses:

307

Une bonne idée pourrait être d'encapsuler tout ce qui se trouve à l'intérieur des groupes, qu'il soit nécessaire de les identifier ou non. De cette façon, vous pouvez les utiliser dans votre chaîne de remplacement. Par exemple:

var pattern = @"(-)(\d+)(-)";
var replaced = Regex.Replace(text, pattern, "$1AA$3"); 

ou en utilisant un MatchEvaluator:

var replaced = Regex.Replace(text, pattern, m => m.Groups[1].Value + "AA" + m.Groups[3].Value);

Une autre façon, légèrement désordonnée, pourrait être d'utiliser un lookbehind / lookahead:

(?<=-)(\d+)(?=-)

bluepnume
la source
17
J'ai modifié votre réponse pour fournir plus d'informations, mais ce que vous avez dit est tout à fait exact. Je ne sais pas comment j'ai raté le fait que je pouvais tout mettre dans des groupes, peu importe si je les utiliserai ou non :) . À mon avis, cette solution est bien meilleure et plus propre que l'utilisation d'anticipation et de regard en arrière.
Oscar Mederos
petite faute de frappe, votre modèle de remplacement devrait être de 1 $ AA 3 $
Myster
1
Pour que cela fonctionne, je devais ajouter .Valueà m.Groups[1]etc.
jbeldock
11
A noter également: si votre texte de remplacement commence par un nombre, la première solution ("$ 1AA $ 3") ne fonctionnera pas comme prévu!
Bertie
2
@OscarMederos vous pouvez également utiliser des groupes non capturants - idéal pour les groupes que vous n'utilisez pas. Dans (?:foo)(bar), $1remplacera bar. plus de détails
Patrick
34

Vous pouvez le faire en utilisant lookahead et lookbehind :

var pattern = @"(?<=-)\d+(?=-)";
var replaced = Regex.Replace(text, pattern, "AA"); 
LukeH
la source
19

J'en avais également besoin et j'ai créé la méthode d'extension suivante pour cela:

public static class RegexExtensions
{
    public static string ReplaceGroup(
        this Regex regex, string input, string groupName, string replacement)
    {
        return regex.Replace(
            input,
            m =>
            {
                var group = m.Groups[groupName];
                var sb = new StringBuilder();
                var previousCaptureEnd = 0;
                foreach (var capture in group.Captures.Cast<Capture>())
                {
                    var currentCaptureEnd =
                        capture.Index + capture.Length - m.Index;
                    var currentCaptureLength =
                        capture.Index - m.Index - previousCaptureEnd;
                    sb.Append(
                        m.Value.Substring(
                            previousCaptureEnd, currentCaptureLength));
                    sb.Append(replacement);
                    previousCaptureEnd = currentCaptureEnd;
                }
                sb.Append(m.Value.Substring(previousCaptureEnd));

                return sb.ToString();
            });
    }
}

Usage:

var input = @"[assembly: AssemblyFileVersion(""2.0.3.0"")][assembly: AssemblyFileVersion(""2.0.3.0"")]";
var regex = new Regex(@"AssemblyFileVersion\(""(?<version>(\d+\.?){4})""\)");


var result = regex.ReplaceGroup(input , "version", "1.2.3");

Résultat:

[assembly: AssemblyFileVersion("1.2.3")][assembly: AssemblyFileVersion("1.2.3")]
Daniel Hilgarth
la source
J'aime cette implémentation mais elle ne remplace pas plusieurs correspondances. J'ai posté une version qui fait
Vladimir
13

Si vous ne souhaitez pas modifier votre modèle, vous pouvez utiliser les propriétés Index de groupe et Longueur d'un groupe correspondant.

var text = "example-123-example";
var pattern = @"-(\d+)-";
var regex = new RegEx(pattern);
var match = regex.Match(text);

var firstPart = text.Substring(0,match.Groups[1].Index);    
var secondPart = text.Substring(match.Groups[1].Index + match.Groups[1].Length);
var fullReplace = firstPart + "AA" + secondPart;
Dick Verweij
la source
Veuillez noter que cela suppose et ne fonctionnera que pour la première occurrence du match.
Bartosz
5

Voici une autre belle option propre qui ne nécessite pas de changer votre modèle.

        var text = "example-123-example";
        var pattern = @"-(\d+)-";

        var replaced = Regex.Replace(text, pattern, (_match) =>
        {
            Group group = _match.Groups[1];
            string replace = "AA";
            return String.Format("{0}{1}{2}", _match.Value.Substring(0, group.Index - _match.Index), replace, _match.Value.Substring(group.Index - _match.Index + group.Length));
        });
cheveux bouclés
la source
0

passez par le codage ci-dessous pour obtenir le remplacement de groupe séparé.

new_bib = Regex.Replace(new_bib, @"(?s)(\\bibitem\[[^\]]+\]\{" + pat4 + @"\})[\s\n\v]*([\\\{\}a-zA-Z\.\s\,\;\\\#\\\$\\\%\\\&\*\@\\\!\\\^+\-\\\=\\\~\\\:\\\" + dblqt + @"\\\;\\\`\\\']{20,70})", delegate(Match mts)
                    {
                           var fg = mts.Groups[0].Value.ToString(); 
                           var fs = mts.Groups[1].Value.ToString();
                           var fss = mts.Groups[2].Value.ToString();
                               fss = Regex.Replace(fss, @"[\\\{\}\\\#\\\$\\\%\\\&\*\@\\\!\\\^+\-\\\=\\\~\\\:\\\" + dblqt + @"\\\;\\\`\\\']+", "");
                           return "<augroup>" + fss + "</augroup>" + fs;
                    }, RegexOptions.IgnoreCase);
BalaS
la source
0

Voici une version similaire à celle de Daniel mais remplaçant plusieurs correspondances:

public static string ReplaceGroup(string input, string pattern, RegexOptions options, string groupName, string replacement)
{
    Match match;
    while ((match = Regex.Match(input, pattern, options)).Success)
    {
        var group = match.Groups[groupName];

        var sb = new StringBuilder();

        // Anything before the match
        if (match.Index > 0)
            sb.Append(input.Substring(0, match.Index));

        // The match itself
        var startIndex = group.Index - match.Index;
        var length = group.Length;
        var original = match.Value;
        var prior = original.Substring(0, startIndex);
        var trailing = original.Substring(startIndex + length);
        sb.Append(prior);
        sb.Append(replacement);
        sb.Append(trailing);

        // Anything after the match
        if (match.Index + match.Length < input.Length)
            sb.Append(input.Substring(match.Index + match.Length));

        input = sb.ToString();
    }

    return input;
Vladimir
la source