6

Within a string, I'm trying to update multiple instances of the same word with different values.

This is an overly simplified example, but given the following string:

"The first car I saw was color, the second car was color and the third car was color"

The first instance of the word color I want to replace with "red", the second instance should be "green" and the third instance should be "blue".

What I thought to try was a regex pattern to find boundried words, interate through a loop and replace them one at a time. See the example code below.

var colors = new List<string>{ "reg", "green", "blue" };
var sentence = "The first car I saw was color, the second car was color and the third car was color";

foreach(var color in colors)
{
    var regex = new Regex("(\b[color]+\b)");
    sentence = regex.Replace(sentence, color, 1);
}

However, the word "color" never gets replaced with the appropriate color name. I can't find what I've been doing wrong.

Andy Evans
  • 6,997
  • 18
  • 72
  • 118

3 Answers3

4

Try a match delegate.

It is an overload of Regex.Replace() that most folks miss. It simply lets you define a, potentially context sensitive, dynamic handler instead of a hardcoded string to replace with, and could have side-effects. The "i++ %" is a modulo operator is used below to simply cycle through the values. You could use a database or a hash table or anything.

var colors = new List<string> { "red", "green", "blue" };
var sentence = "The first car I saw was color, the second car was color and the third car was color";
int i = 0;
Regex.Replace(sentence, @"\bcolor\b", (m) => { return colors[i++ % colors.Count]; })

This solution works for an arbitrary number of replacements, which is more typical (global replace).

codenheim
  • 20,467
  • 1
  • 59
  • 80
  • That's a lovely delegate, I see that you're no stranger to the `regex` tag. :) – zx81 Jun 19 '14 at 00:19
  • @zx81: Thanks! Yes, in my experience, most folks don't even realize the .NET Regex library supports a match delegate. Though I prefer regex implemented as syntax rather than API, like Perl. It is what I've done with Cola actually, though I haven't decided how I want to map the regex delegate idiom to syntax yet. – codenheim Jun 19 '14 at 00:30
  • `Cola` must be an absorbing project, I can't even imagine what it would be like to father and nurture something of that scope... I love the power of lambdas in regex. I had a lot of fun with [this one](http://stackoverflow.com/questions/23589174/match-or-replace-a-pattern-except-in-situations-s1-s2-s3-etc/23589204#23589204), as it relies on lambdas in nearly all regex flavors (no need in Perl and PCRE). The example in your answer was particularly nice. – zx81 Jun 19 '14 at 00:34
  • Nice answer over there! – codenheim Jun 19 '14 at 00:54
2

The problem is that in your example, color isn't always preceded and followed by a non-word character. For your example, this worked for me:

var regex = new Regex("\b?(color)\b?");

So this:

var colors = new List<string>{ "red", "green", "blue" };
var sentence = "The first car I saw was color, the second car was color and the third car was color";

foreach(var color in colors)
{
    var regex = new Regex("\b?(color)\b?");
    sentence = regex.Replace(sentence, color, 1);
}

Produces this:

The first car I saw was red, the second car was green and the third car was blue

Sven Grosen
  • 5,616
  • 3
  • 30
  • 52
  • 1
    `\b?` is superfluous - either it is word boundary, or it isn't - might as well use `"(color)"` – Uri Agassi Jun 06 '14 at 20:06
  • @UriAgassi I won't argue with you, I'd actually lean towards using Steven Wexler's [answer](http://stackoverflow.com/a/24089785/1346943) – Sven Grosen Jun 06 '14 at 20:08
  • The `var regex` is loop invariant, so take it outside of the loop. – ClickRick Jun 06 '14 at 20:08
  • For further optimization you can use `RegexOptions.Compiled`, but in all honesty these optimization probably don't matter much in practice. Note, `RegexOptions.Compiled` adds time to your build. See http://blog.codinghorror.com/to-compile-or-not-to-compile/ for more info about whether or not to use `RegexOptions.Compiled`. – Steven Wexler Jun 06 '14 at 20:11
  • The downside to this solution is that it fails if your sentence has more than 3 instances of the color pattern to replace. – codenheim Jun 06 '14 at 21:07
1

I try to stay away from Regex whenever possible. It has it's place, but not for simple cases like this IMHO :)

public static class StringHelpers
{
    //Copied from http://stackoverflow.com/questions/141045/how-do-i-replace-the-first-instance-of-a-string-in-net/141076#141076
    public static string ReplaceFirst(this string text, string search, string replace)
    {
        int pos = text.IndexOf(search);
        if (pos < 0)
        {
            return text;
        }
        return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
    }
}


var colors = new List<string>{ "red", "green", "blue" };
string sentence = colors.Aggregate(
    seed: "The first car I saw was color, the second car was color and the third car was color", 
    func: (agg, color) => agg.ReplaceFirst("color", color));
Steven Wexler
  • 16,589
  • 8
  • 53
  • 80