Pozwól, że przedstawię moją sprawę, a jeśli chcesz, możesz mnie rozerwać na strzępy.
Regex nie jest odpowiedzią na ten problem - relatywnie zbyt wolny i głodny pamięci.
StringBuilder jest znacznie lepszy niż string-string.
Ponieważ będzie to metoda rozszerzenia w celu uzupełnienia string.Replace
, uważam, że ważne jest dopasowanie sposobu, w jaki to działa - dlatego ważne jest zgłaszanie wyjątków dla tych samych problemów argumentów, podobnie jak zwracanie oryginalnego ciągu, jeśli nie dokonano zamiany.
Uważam, że posiadanie parametru StringComparison nie jest dobrym pomysłem. Próbowałem, ale przypadek testowy pierwotnie wspomniany przez Michaela-Liu wykazał problem:
[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]
Podczas gdy IndexOf będzie pasować, istnieje niedopasowanie między długością dopasowania w ciągu źródłowym (1) a oldValue.Length (2). Przejawiało się to przez spowodowanie IndexOutOfRange w niektórych innych rozwiązaniach, gdy oldValue.Length został dodany do bieżącej pozycji dopasowania i nie mogłem znaleźć sposobu na obejście tego. Regex i tak nie pasuje do przypadku, więc wybrałem pragmatyczne rozwiązanie polegające na użyciu tylko StringComparison.OrdinalIgnoreCase
dla mojego rozwiązania.
Mój kod jest podobny do innych odpowiedzi, ale moim zdziwieniem jest to, że szukam dopasowania, zanim podejmę trud tworzenia StringBuilder
. Jeśli nie zostanie znaleziony, można uniknąć potencjalnie dużej alokacji. Kod staje się następnie do{...}while
zamiastwhile{...}
Zrobiłem kilka obszernych testów w stosunku do innych odpowiedzi, które pojawiły się ułamkowo szybciej i zużyły nieco mniej pamięci.
public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
{
if (str == null) throw new ArgumentNullException(nameof(str));
if (oldValue == null) throw new ArgumentNullException(nameof(oldValue));
if (oldValue.Length == 0) throw new ArgumentException("String cannot be of zero length.", nameof(oldValue));
var position = str.IndexOf(oldValue, 0, StringComparison.OrdinalIgnoreCase);
if (position == -1) return str;
var sb = new StringBuilder(str.Length);
var lastPosition = 0;
do
{
sb.Append(str, lastPosition, position - lastPosition);
sb.Append(newValue);
} while ((position = str.IndexOf(oldValue, lastPosition = position + oldValue.Length, StringComparison.OrdinalIgnoreCase)) != -1);
sb.Append(str, lastPosition, str.Length - lastPosition);
return sb.ToString();
}