tags:

views:

852

answers:

5

I'm just beginning to use Regex so bear with my terminology. I have a regex pattern that is working properly on a string. The string could be in the format "text [pattern] text". Therefore, I also have a regex pattern that negates the first pattern. If I print out the results from each of the matches everything is shown correctly.

The problem I'm having is I want to add text into the string and it changes the index of matches in a regex MatchCollection. For example, if I wanted to enclose the found match in "td" match "/td"" tags I have the following code:

Regex r = new Regex(negRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            MatchCollection mc = r.Matches(text);
            if (mc.Count > 0)
            {
                for (int i = 0; i < mc.Count; i++)
                {
                    text = text.Remove(mc[i].Index, mc[i].Length);
                    text = text.Insert(mc[i].Index, "<td>" + mc[i].Value + "</td>");
                }                
            }

This works great for the first match. But as you'd expect the mc[i].Index is no longer valid because the string has changed. Therefore, I tried to search for just a single match in the for loop for the amount of matches I would expect (mc.Count), but then I keep finding the first match.

So hopefully without introducing more regex to make sure it's not the first match and with keeping everything in one string, does anybody have any input on how I could accomplish this? Thanks for your input.

Edit: Thank you all for your responses, I appreciate all of them.

A: 

Keep a counter before the loop starts, and add the amount of characters you inserted every time. IE:

            Regex r = new Regex(negRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            MatchCollection mc = r.Matches(text);

                int counter = 0;
                for (int i = 0; i < mc.Count; i++)
                {
                    text = text.Remove(mc[i].Index + counter, mc[i].Length);
                    text = text.Insert(mc[i].Index + counter, "<td>" + mc[i].Value + "</td>");
                    counter += ("<td>" +  "</td>").Length;
                }

I haven't tested this, but it SHOULD work.

BFree
+1  A: 
   static string Tabulate(Match m) 
   {
      return "<td>" + m.ToString() + "</td>";
   }

   static void Replace() 
   {
      string text = "your text";
      string result = Regex.Replace(text, "your_regexp", new MatchEvaluator(Tabulate));
   }
Quassnoi
better to add some kind of explanation along with the code...
annakata
+3  A: 

It can be as simple as:-

  string newString = Regex.Replace("abc", "b", "<td>${0}</td>");

Results in a<td>b</td>c.

In your case:-

Regex r = new Regex(negRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
text = r.Replace(text, "<td>${0}</td>");

Will replace all occurance of negRegexPattern with the content of that match surrounded by the td element.

AnthonyWJones
Such a simple and elegant solution, thanks!
Sean
+1  A: 

Although I agree that the Regex.Replace answer above is the best choice, just to answer the question you asked, how about replacing from the last match to the first. This way your string grows beyond the "previous" match so the earlier matches indexes will still be valid.

for (int i = mc.Count - 1; i > 0; --i)
Tom Faust
+1  A: 

You can try something like this:

Regex.Replace(input, pattern, match =>
{
   return "<tr>" + match.Value + "</tr>";
});
idursun