Aritro Sen
Aritro Sen

Reputation: 357

Replace only 'n' occurences of a substring in a string in C#

I have a input string like -

abbdabab

How to replace only the 2nd, 3rd and subsequent occurances of the substring "ab" with any random string like "x" keeping the original string intact. Example in this case -

1st Output - xbdabab 2nd Output - abbdxab 3rd Output - abbdabx and so on...

I have tried using Regex like -

int occCount = Regex.Matches("abbdabab", "ab").Count;

if (occCount > 1)
{
    for (int i = 1; i <= occCount; i++)
    {
        Regex regReplace = new Regex("ab");
        string modifiedValue = regReplace.Replace("abbdabab", "x", i);
        //decodedMessages.Add(modifiedValue);
    }
 }

Here I am able to get the 1st output when the counter i value is 1 but not able to get the subsequent results. Is there any overloaded Replace method which could achieve this ? Or Can anyone help me in pointing where I might have gone wrong?

Upvotes: 0

Views: 404

Answers (3)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 627128

You may use a dynamically built regex to be used with regex.Replace directly:

var s = "abbdabab";
var idx = 1; // First = 1, Second = 2
var search = "ab";
var repl = "x";
var pat = new Regex($@"(?s)((?:{search}.*?){{{idx-1}}}.*?){search}"); // ((?:ab.*?){0}.*?)ab
Console.WriteLine(pat.Replace(s, $"${{1}}{repl}", 1));

See the C# demo

The pattern will look like ((?:ab.*?){0}.*?)ab and will match

  • (?s) - RegexOptions.Singleline to make . also match newlines
  • ((?:ab.*?){0}.*?) - Group 1 (later, this value will be put back into the result with ${1} backreference)
    • (?:ab.*?){0} - 0 occurrences of ab followed with any 0+ chars as few as possible
    • .*? - any 0+ chars as few as possible
  • ab - the search string/pattern.

The last argument to pat.Replace is 1, so that only the first occurrence could be replaced.

If search is a literal text, you need to use var search = Regex.Escape("a+b");.

If the repl can have $, add repl = repl.Replace("$", "$$");.

Upvotes: 0

Sweeper
Sweeper

Reputation: 273135

You can use a StringBuilder:

string s = "abbdabab";
var matches = Regex.Matches(s, "ab");
StringBuilder sb = new StringBuilder(s);
var m = matches[0]; // 0 for first output, 1 for second output, and so on
sb.Remove(m.Index, m.Length);
sb.Insert(m.Index, "x");
var result = sb.ToString();
Console.WriteLine(result);

Upvotes: 1

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186813

You can try IndexOf instead of regular expressions:

string source = "abbdabab";
string toFind = "ab";
string toSet = "X";

for (int index = source.IndexOf(toFind); 
     index >= 0; 
     index = source.IndexOf(toFind, index + 1)) {
  string result = source.Substring(0, index) + 
                  toSet + 
                  source.Substring(index + toFind.Length);

  Console.WriteLine(result);
}

Outcome:

Xbdabab
abbdXab
abbdabX

Upvotes: 2

Related Questions