开发者

Count regex replaces (C#)

开发者 https://www.devze.com 2023-02-10 22:37 出处:网络
Is there a way to count the number of replacements a Regex.Replace call makes? E.g. for Regex.Replace(\"aaa\", \"a\", \"b\"); I 开发者_运维问答want to get the number 3 out (result is \"bbb\"); for Re

Is there a way to count the number of replacements a Regex.Replace call makes?

E.g. for Regex.Replace("aaa", "a", "b"); I 开发者_运维问答want to get the number 3 out (result is "bbb"); for Regex.Replace("aaa", "(?<test>aa?)", "${test}b"); I want to get the number 2 out (result is "aabab").

Ways I can think to do this:

  1. Use a MatchEvaluator that increments a captured variable, doing the replacement manually
  2. Get a MatchCollection and iterate it, doing the replacement manually and keeping a count
  3. Search first and get a MatchCollection, get the count from that, then do a separate replace

Methods 1 and 2 require manual parsing of $ replacements, method 3 requires regex matching the string twice. Is there a better way.


Thanks to both Chevex and Guffa. I started looking for a better way to get the results and found that there is a Result method on the Match class that does the substitution. That's the missing piece of the jigsaw. Example code below:

using System.Text.RegularExpressions;

namespace regexrep
{
    class Program
    {
        static int Main(string[] args)
        {
            string fileText = System.IO.File.ReadAllText(args[0]);
            int matchCount = 0;
            string newText = Regex.Replace(fileText, args[1],
                (match) =>
                {
                    matchCount++;
                    return match.Result(args[2]);
                });
            System.IO.File.WriteAllText(args[0], newText);
            return matchCount;
        }
    }
}

With a file test.txt containing aaa, the command line regexrep test.txt "(?<test>aa?)" ${test}b will set %errorlevel% to 2 and change the text to aabab.


You can use a MatchEvaluator that runs for each replacement, that way you can count how many times it occurs:

int cnt = 0;
string result = Regex.Replace("aaa", "a", m => {
  cnt++;
  return "b";
});

The second case is trickier as you have to produce the same result as the replacement pattern would:

int cnt = 0;
string result = Regex.Replace("aaa", "(?<test>aa?)", m => {
  cnt++;
  return m.Groups["test"] + "b";
});


This should do it.

     int count = 0;
     string text = Regex.Replace(text,
          @"(((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)", //Example expression. This one captures URLs.
          match =>
          {
               string replacementValue = String.Format("<a href='{0}'>{0}</a>", match.Value);
               count++;
               return replacementValue;
          });

I am not on my dev computer so I can't do it right now, but I am going to experiment later and see if there is a way to do this with lambda expressions instead of declaring the method IncrementCount() just to increment an int.

EDIT modified to use a lambda expression instead of declaring another method.

EDIT2 If you don't know the pattern in advance, you can still get all the groupings (The $ groups you refer to) within the match object as they are included as a GroupCollection. Like so:

     int count = 0;
     string text = Regex.Replace(text,
          @"(((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)", //Example expression. This one captures URLs.
          match =>
          {
               string replacementValue = String.Format("<a href='{0}'>{0}</a>", match.Value);
               count++;
               foreach (Group g in match.Groups)
               {
                    g.Value; //Do stuff with g.Value
               }
               return replacementValue;
          });
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号