The regex match evaluator gives one the ability to do a post process match in step for each match found. It is a handy way to normalize the match before sending it on. In that process could easily change or alter the match when needed. It also allows us to eat the match and have it return nothing!

Here is an example which I had from the boards. The user wanted to use regex replace to remove all alphabetic characters but return all numbers and a decimal place. But he had situations where there were two decimals. In that situation then only return the first one.

12abc.def34 becomes 12.34

.56a.d78 becomes .5678

Here is the code to accomplish that

// Only worry about decimals and letters.
string pattern = @"
(?<Decimal>\.) |       # Check for decimal
(?<Letter>[A-Za-z]+)    # Check for letter
";

string data = "0.ab.c1d.23"; // We want 0.123

int decimalPointCount = 0;

// Here is the Match Evaluator for Post Processing.
// We will eat the letters and return the decimals.
// The match evaluator will feed us every match found as it finds it.
MatchEvaluator CatchMultipleDecimals = delegate( Match m )
{
// Check for a decimal match only and return only the first one found
if (string.IsNullOrEmpty(m.Groups["Decimal"].Value) == false)
{
   if (decimalPointCount++ > 0) // We have gone over…return nothing!
      return string.Empty;
   else
      return m.Groups["Decimal"].Value; // Return the .
}

// We are only capturing text...so return nothing
// on any other match we may get.
   return string.Empty;

};

MatchEvaluator myEvaluator = new MatchEvaluator(CatchMultipleDecimals);

// Remember IgnoreWhiteSpace option only applies to how the regex parser
// processes the pattern and not the data. Since I have created my
// pattern split over multiple lines for readability I need to tell
// the regex parser to strip all whitespace from my pattern *before* it
// looks at any data. It has nothing to do with how the data is matched or processed.
Console.WriteLine(Regex.Replace(data, pattern, myEvaluator, RegexOptions.IgnorePatternWhitespace));

// Outputs 0.123 from the text 0.ab.c1d.23

Explanation

  • Line 2  : The pattern will only capture a decimal point or letter(s).
  • Line 14  : Here is the delegate that has the code which will be called whenever a match occurs.
  • Line 17 : Since we have placed the items into groups, we will check the Decimal group for any data.  If data exists, we return a decimal point only once, otherwise we return string.empty.
  • Line 21 : We could check for the Letter group, but that is not needed. Since the decimal is handled already, we will eat whatever is at this point and return string.empty to show that this is a non match.
  • Line 27 : We use regex replace to return any numbers with only one decimal thanks to the Match Evaluator we have created and used.
Share