4

I am using a Regex with a MatchEvaluator delegate to process a format string, e.g. "Time: %t, bytes: %b" would replace the "%t" with a time stamp, and the "%b" with a bytes count. Needless to say, there are loads of other options!

To do this, I use:

    Regex regex = new Regex("%((?<BytesCompleted>b)|(?<BytesRemaining>B)|(?<TimeStamp>t))");
    string s = "%bhello%t(HH:MM:SS)%P";
    string r = regex.Replace(s, new MatchEvaluator(ReplaceMatch));

and

string ReplaceMatch(Match m)
{
    ... Handle the match replacement.
}

What would be nice is if I could use the Regex group name (or even the number, I'm not that fussy) in the delegate instead of comparing against the raw match to find out which match this is:

string ReplaceMatch(Match m)
{
    ...
    case "%t":
    ...
    case "%b";
    ...
}

Is pretty ugly; I would like to use

string ReplaceMatch(Match m)
{
    ...
    case "BytesCompleted":
    ...
    case "TimeStamp":
    ...
}

I can't see anything obvious via the debugger, or via google. Any ideas?

Sam Leach
  • 12,746
  • 9
  • 45
  • 73
ggcodes
  • 2,859
  • 6
  • 21
  • 34

3 Answers3

0

It would be nice to be able to use the group name in the switch; unfortunately the Group object doesn't have a Name property (and neither does its base class Capture) so the best you'll be able to do is the following:

string ReplaceMatch(Match m)
{
    if (m.Groups["BytesCompleted"].Success) // ...
    else if (m.Groups["BytesRemaining"].Success) // ...
    // ...
}
Linus Caldwell
  • 10,908
  • 12
  • 46
  • 58
james raygan
  • 681
  • 5
  • 10
  • 28
0

You can use Regex.GroupNameFromNumber instance method, and this unfortunately means that the match-evaluator method requires a reference to the Regex object:

string ReplaceMatch(Match m)
{
    for (int i = 0; i < m.Groups.Count; i++)
    {
        string groupName = _regex.GroupNameFromNumber(i);
        switch (groupName)
        {
            case "BytesCompleted":
                // do something
                break;
            case "BytesRemaining":
                // do somehting else
                break;
            ...
        }
    }
    ...
}

Here I assumed that the Regex object is accessible through the _regex variable.

Sina Iravanian
  • 16,011
  • 4
  • 34
  • 45
0

We'll need to combine both Sina's and james' answers. We need to enumerate the groups to get the index and check for group success. Then we use the index to get group name. I have expanded a bit into a self-explaining test that uses dictionary to replace matched substrings. With a little more support for group names in the framework, this would have been so much easier.

Also see another answer that might work for you: https://stackoverflow.com/a/1381163/481812

[Test]
public void ExploreRxReplace()
{
    var txt = " ABC XYZ DEF ";
    var exp = " *** XYZ xxx ";
    var rx = new Regex(@"\s*(?<abc>ABC)\s*|\s*(?<def>DEF)\s*");
    var data = new Dictionary<string, string>() { { "abc", "***" }, { "def", "xxx" } };

    var txt2 = rx.Replace(txt, (m) =>
    {
        var sb = new StringBuilder();
        var pos = m.Index;

        for (var idx = 1; idx < m.Groups.Count; idx++)
        {
            var group = m.Groups[idx];

            if (!group.Success)
            {
                // ignore non-matching group
                continue;
            }

            var name = rx.GroupNameFromNumber(idx);

            // append what's before
            sb.Append(txt.Substring(pos, group.Index - pos));

            string value = null;
            if (group.Success && data.TryGetValue(name, out value))
            {
                // append dictionary value
                sb.Append(value);
            }
            else
            {
                // append group value
                sb.Append(group.Value);
            }

            pos = group.Index + group.Length;
        }

        // append what's after
        sb.Append(txt.Substring(pos, m.Index + m.Length - pos));

        return sb.ToString();
    });

    Assert.AreEqual(exp, txt2);
}
Community
  • 1
  • 1
Robert Cutajar
  • 3,181
  • 1
  • 30
  • 42