Skip to content

Commit e1d9985

Browse files
committed
Merge pull request umbraco#367 from lctjohn/7.1.1-U4-2635
fix for: U4-2635 Replace [...] method only replaces first ocurrence of oldString
2 parents b60aa30 + b412729 commit e1d9985

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

src/Umbraco.Core/StringExtensions.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,17 +1191,18 @@ public static string ToSafeFileName(this string text, CultureInfo culture)
11911191
/// <returns>Updated string</returns>
11921192
public static string Replace(this string source, string oldString, string newString, StringComparison stringComparison)
11931193
{
1194-
var index = source.IndexOf(oldString, stringComparison);
1194+
// This initialisation ensures the first check starts at index zero of the source. On successive checks for
1195+
// a match, the source is skipped to immediately after the last replaced occurrence for efficiency
1196+
// and to avoid infinite loops when oldString and newString compare equal.
1197+
int index = -1 * newString.Length;
11951198

1196-
// Determine if we found a match
1197-
var matchFound = index >= 0;
1198-
1199-
if (matchFound)
1199+
// Determine if there are any matches left in source, starting from just after the result of replacing the last match.
1200+
while((index = source.IndexOf(oldString, index + newString.Length, stringComparison)) >= 0)
12001201
{
1201-
// Remove the old text
1202+
// Remove the old text.
12021203
source = source.Remove(index, oldString.Length);
12031204

1204-
// Add the replacemenet text
1205+
// Add the replacemenet text.
12051206
source = source.Insert(index, newString);
12061207
}
12071208

src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ public void TrimStart(string input, string forTrimming, string shouldBe)
8080
Assert.AreEqual(shouldBe, trimmed);
8181
}
8282

83+
[TestCase("Hello this is my string", "hello", "replaced", "replaced this is my string", StringComparison.CurrentCultureIgnoreCase)]
84+
[TestCase("Hello this is hello my string", "hello", "replaced", "replaced this is replaced my string", StringComparison.CurrentCultureIgnoreCase)]
85+
[TestCase("Hello this is my string", "nonexistent", "replaced", "Hello this is my string", StringComparison.CurrentCultureIgnoreCase)]
86+
[TestCase("Hellohello this is my string", "hello", "replaced", "replacedreplaced this is my string", StringComparison.CurrentCultureIgnoreCase)]
87+
// Ensure replacing with the same string doesn't cause infinite loop.
88+
[TestCase("Hello this is my string", "hello", "hello", "hello this is my string", StringComparison.CurrentCultureIgnoreCase)]
89+
public void ReplaceWithStringComparison(string input, string oldString, string newString, string shouldBe, StringComparison stringComparison)
90+
{
91+
var replaced = input.Replace(oldString, newString, stringComparison);
92+
Assert.AreEqual(shouldBe, replaced);
93+
}
94+
8395
[TestCase(null, null)]
8496
[TestCase("", "")]
8597
[TestCase("x", "X")]

0 commit comments

Comments
 (0)