1

I have this code:

string a = "**MustbeReplaced**asdgasfsff**MustbeReplaced**asdfafasfsa";
//MustbeReplaced should be Replaced1, Replaced2, and so on
a = a.Replace("MustbeReplaced", "Replaced"); 

Every time there is a string replacement, I would like to replace it with a dynamic value. For instance, the output of above string should be:

"**Replaced1**asdgasfsff**Replaced2**asdfafasfsa"

I know about String.Replace, but to the best of my knowledge, it's only for static String replacement.

Is there any way to do this?

Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
Yusril Maulidan Raji
  • 1,682
  • 1
  • 21
  • 46
  • Find first occurrence with `IndexOf`, replace, find next occurrence starting at previous position + replacement length, repeat until `IndexOf` returns `-1` – Manfred Radlwimmer Sep 20 '16 at 07:57
  • Possible duplicate of [How do I replace the \*first instance\* of a string in .NET?](http://stackoverflow.com/questions/141045/how-do-i-replace-the-first-instance-of-a-string-in-net) – Esko Sep 20 '16 at 07:58
  • 1
    Break the string into string array using key "MustbeReplaced" as seperator. Then run for loop on array. – Knu8 Sep 20 '16 at 07:59
  • @Esko No, I suppose that's a different case. In my case, I need to not only replace the first occurrence, but also all the occurrences. – Yusril Maulidan Raji Sep 20 '16 at 08:05

5 Answers5

5

You can use the Regex class and provide a delegate that will be called once for each match. It needs to return the string to replace the matched text with.

You simply have to declare a variable holding your counter:

string a = "**MustbeReplaced**asdgasfsff**MustbeReplaced**asdfafasfsa";
int replacementIndex = 0;
string b = Regex.Replace(a, "MustbeReplaced", match =>
{
    replacementIndex++;
    return $"Replaced{replacementIndex}";
});

After running this, b will contain this:

**Replaced1**asdgasfsff**Replaced2**asdfafasfsa

Caution: Since you're now using the Regex class, be aware of all the special characters that Regex will use to augment the pattern away from simple character-by-character matching. If you're replacing text containing symbols like asterixes, question marks, parenthesis, etc. then you need to escape those.

Luckily we can simply ask the Regex class to do that for us:

string a = "**Mustbe?Replaced**asdgasfsff**Mustbe?Replaced**asdfafasfsa";
int replacementIndex = 0;
string b = Regex.Replace(a, Regex.Escape("Mustbe?Replaced"), match =>
{
    replacementIndex++;
    return $"Replaced{replacementIndex}";
});
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
2

Use the Regex.Replace(String, MatchEvaluator) overload. The corresponding MSDN page contains an example which almost exactly matches your requirement.

Here's an even simpler example:

var input = "MustBeReplaced A MustBeReplaced B";

int i = 1;
MatchEvaluator evaluator = (m) => "Replaced " + (i++);

// yields Replaced 1 A Replaced 2 B
var result = Regex.Replace(input, "MustBeReplaced", evaluator);
Heinzi
  • 167,459
  • 57
  • 363
  • 519
1

You can either use regex as already shown or this more efficient but less readable pure string methods approach:

string a = "**MustbeReplaced**asdgasfsff**MustbeReplaced**asdfafasfsa";
int matchCount = 0, index = 0;
while ((index = a.IndexOf("MustbeReplaced", index, StringComparison.Ordinal)) >= 0)
{
    a = a.Remove(index, "MustbeReplaced".Length).Insert(index, "Replaced" + ++matchCount);
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Why is this more efficient? – Lasse V. Karlsen Sep 20 '16 at 08:04
  • @LasseV.Karlsen: because string methods are more efficient than regex – Tim Schmelter Sep 20 '16 at 08:06
  • Wouldn't this then be far better with the use of a `StringBuilder` as well? – Lasse V. Karlsen Sep 20 '16 at 08:06
  • @LasseV.Karlsen: it depends. I've tested the regex approach with mine and there is less difference than i've thought. Regex needed 400 millis whereas the string-method approach needed 292 milliseconds on 100000 calls. – Tim Schmelter Sep 20 '16 at 08:12
  • My tests showed the exact opposite. I doubled the string 5 times to generate a starting input of 1824 characters, on my machine the regex version ran in 5 milliseconds, the remove/insert loop in 19, with 100 iterations. A regex is table-driven for this kind of basic pattern matching and the Regex.Replace method uses a StringBuilder internally to build up the result so this doesn't really surprise me. I think GC is killing the remove/insert version. – Lasse V. Karlsen Sep 20 '16 at 08:14
  • The regex will perform better the longer the string is. So a StringBuilder approach would be better if it exceeds a certain length, say 200 characters. – Tim Schmelter Sep 20 '16 at 08:18
0

Could be done like:

        string source = "**MustbeReplaced**asdgasfsff**MustbeReplaced**asdfafasfsa";
        int count = 0;
        var result = Regex.Replace(source, "MustbeReplaced", m => "Replaced" + (++count));
bm4tu
  • 39
  • 2
0

Want to avoid Regex? Want more performance? Use this code. This code uses only at one time a StringBuilder.

    public static string ReplaceWithNumberPrefix(this string source, string oldString, string newString, int numberPrefixStart)
    {
        var oldStringLength = oldString.Length;

        var indexes = new List<int>();
        var p = source.IndexOf(oldString);                    
        if (p >= 0)
            do
            {
                indexes.Add(p);
                p = source.IndexOf(oldString, p + 1);                    
            } while (p >= 0);

        var builder = new System.Text.StringBuilder(source);
        var trailingDifference = 0;
        for (int i = 0; i < indexes.Count; i++)
        {
            var replacement = string.Concat(newString, numberPrefixStart);
            var startIndex = indexes[i] + trailingDifference;
            builder.Replace(oldString, replacement, startIndex, oldStringLength);

            numberPrefixStart++;
            trailingDifference = trailingDifference + replacement.Length - oldStringLength;
        }

        return builder.ToString();
    }
Roberto B
  • 542
  • 5
  • 13