9

I'm parsing a text using C# regex. I want to replace only one specific group in for each match. Here how I'm doing it:

void Replace(){
  string newText = Regex.Replace(File.ReadAllText(sourceFile), myRegex, Matcher, RegexOptions.Singleline);
  //.......   
}

void string Matcher(Match m){
  // how do I replace m.Groups[2] with "replacedText"?
  return ""; // I want to return m.Value with replaced m.Group[2]
}
Alan Coromano
  • 24,958
  • 53
  • 135
  • 205

6 Answers6

12

This should do it:

string Matcher(Match m)
{
    if (m.Groups.Count < 3)
    {
        return m.Value;
    }

    return string.Join("",  m.Groups
                             .OfType<Group>() //for LINQ
                             .Select((g, i) => i == 2 ? "replacedText" : g.Value)
                             .Skip(1) //for Groups[0]
                             .ToArray());
}

Example: http://rextester.com/DLGVPA38953

EDIT: Although the above is the answer to your question as written, you may find zero-width lookarounds simpler for your actual scenario:

Regex.Replace(input, @"(?<=e)l+(?=o)", replacement)

Example: http://rextester.com/SOWWS24307

Justin Morgan - On strike
  • 30,035
  • 12
  • 80
  • 104
  • @AlanDert - That's probably an issue with your regex. [Here's an example of this code running online.](http://rextester.com/GFFO18933) You might want to try tweaking your regex pattern. – Justin Morgan - On strike Oct 22 '12 at 08:41
  • An issue with this solution is that is requires the whole match to be contained in groups whereas it makes sense to only have particular parts of a match in Groups to allow easier access to relevant parts. – Sellorio Jul 19 '17 at 23:59
  • @Sellorio - You're right, and I think that's what I was getting at in my edit about lookarounds (it's been a while since I wrote it). – Justin Morgan - On strike Oct 29 '19 at 21:01
5

You can use MatchEvaluator: Try This.

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

I found one post in stackoverflow related to this: watch This

Community
  • 1
  • 1
Bhavik Patel
  • 752
  • 1
  • 5
  • 20
  • 2
    But OP already has a MatchEvaluator `void string Matcher(Match m){}` – L.B Oct 22 '12 at 06:52
  • 1
    In [that other answer](http://stackoverflow.com/a/6005637/20938), the correct solution was the first one: replace it with the string `"$1AA$3"`. There was no need for a MatchEvaluator there, and there's probably no need for one here, either. – Alan Moore Oct 22 '12 at 08:57
  • @AlanMoore, I prefer the MatchEvaluator because this method gets more complicated sometimes. Consider this example, what if you wanted to replace the first group with the second one in this regex: [`a(b)c(d)e`](https://regex101.com/r/qNnLId/1)? You'd have to put `a`, `c` and `e` in groups too so you can combine the groups afterward. – 41686d6564 stands w. Palestine May 17 '18 at 01:36
5

How about this?

    static string Matcher(Match m)
    {
        var group = m.Groups[2];
        var startIndex = group.Index - m.Index;
        var length = group.Length;
        var original = m.Value;
        var prior = original.Substring(0, startIndex);
        var trailing = original.Substring(startIndex + length);
        return string.Concat(prior, "replacedText", trailing);
    }
mlorbetske
  • 5,529
  • 2
  • 28
  • 40
2

Regex.Replace will always replace everything that the provided regex has matched.

You have two possibilities:

  1. Match only what you want to replace

    or

  2. Insert the parts of the original string which has been matched, but should be kept, into the replacement string, using capturing groups.

You provided not enough information to answer your question more specific. E.g. do you really need a MatchEvaluator? This is only needed, if you want to provide an individual replacement string for each match.

Community
  • 1
  • 1
stema
  • 90,351
  • 20
  • 107
  • 135
0
    private string ReplaceText(string originalText)
    {
        string replacedText = null;

        Regex regEx = new Regex("[your expression here]");

        Match match = regEx.Match(originalText);

        if (match.Success)
        {
            Group matchGroup = match.Groups[2];

            //[first part of original string] + [replaced text] + [last part of original string]
            replacedText =
                $"{originalText.Substring(0, matchGroup.Index)}[Your replaced string here]{originalText.Substring(matchGroup.Index + matchGroup.Length)}";

        }
        else
            replacedText = originalText;

        return replacedText;
    }
  • please clarify your answer – Alex Filatov Jun 26 '17 at 19:45
  • @AlexFilatov Sure which part? You would call it like so ReplaceText("Hello Country") normally these would be passed in as a parameter [Your expression here] is what you are looking for is "Country" ["Your replaced string here"] is "World" what you'd get back returned to "Hello World" if nothing is found you'd get back your original string. – Raymond Jun 28 '17 at 02:49
0

The below code will take a match and construct a replacement string based on the groupValues collection. This collection specifies a Group index and the text to replace it's value with.

Unlike other solutions, it does not require the entire match to be contained in groups, only the parts of the match that you want to have in groups.

public static string ReplaceGroups(Match match, Dictionary<int, string> groupValues)
{
    StringBuilder result = new StringBuilder();

    int currentIndex = 0;
    int offset = 0;

    foreach (KeyValuePair<int, string> replaceGroup in groupValues.OrderBy(x => x.Key))
    {
        Group group = match.Groups[replaceGroup.Key];

        if (currentIndex < group.Index)
        {
            result.Append(match.Value.Substring(currentIndex, group.Index - match.Index - currentIndex));
        }

        result.Append(replaceGroup.Value);

        offset += replaceGroup.Value.Length - group.Length;
        currentIndex = group.Index - match.Index + group.Length;
    }

    return result.ToString();
}
Sellorio
  • 1,806
  • 1
  • 16
  • 32