Coder Perfect

Regex is only used to replace a few groups.

Problem

Let’s pretend I’ve got the following regex:

-(\d+)-

and I want to replace Group 1 (d+) with AA using C#, to get:

-AA-

I’m now replacing it with:

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

But this bothers me since, if I alter the pattern to match _(d+)_ instead, I’ll have to update the replacement string to _AA_ as well, which violates the DRY principle.

I’m looking for anything along the lines of:

Keep the matched text the same, but replace Group 1 with this text and Group 2 with another…

That was merely an example. I’m just searching for a generic technique to accomplish what I’ve stated previously.

It should be suitable for:

any(d+)additional text and any pattern you can think of

I only want to replace the groupings and leave the rest of the match alone.

Asked by Oscar Mederos

Solution #1

A good idea could be to encapsulate everything inside groups, no matter if need to identify them or not. That way you can use them in your replacement string. For example:

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

Alternatively, you can use a MatchEvaluator:

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

Another, but more clumsy option is to use a lookbehind/lookahead:

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

Answered by bluepnume

Solution #2

Lookahead and lookbehind can be used to accomplish this:

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

Answered by LukeH

Solution #3

I had a need for this as well, therefore I wrote the following extension method:

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");

Result:

[assembly: AssemblyFileVersion("1.2.3")][assembly: AssemblyFileVersion("1.2.3")]

Answered by Daniel Hilgarth

Solution #4

You can use the Group Index and Length properties of a matched group if you don’t want to update your pattern.

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;

Answered by Dick Verweij

Solution #5

Here’s another simple solution that won’t require you to change your design.

        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));
        });

Answered by curlyhairedgenius

Post is based on https://stackoverflow.com/questions/6005609/replace-only-some-groups-with-regex