Deixe-me fazer o meu caso e então você pode me rasgar em pedaços, se quiser.
Regex não é a resposta para esse problema - muito lento e com fome de memória, relativamente falando.
StringBuilder é muito melhor que manipular string.
Como esse será um método de extensão para complementar string.Replace
, acredito que é importante combinar como isso funciona - portanto, lançar exceções para os mesmos problemas de argumento é importante, assim como retornar a string original se uma substituição não tiver sido feita.
Acredito que ter um parâmetro StringComparison não é uma boa ideia. Eu tentei, mas o caso de teste mencionado originalmente por michael-liu mostrou um problema: -
[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]
Enquanto IndexOf corresponderá, há uma incompatibilidade entre o comprimento da correspondência na cadeia de origem (1) e no oldValue.Length (2). Isso se manifestou causando IndexOutOfRange em algumas outras soluções quando oldValue.Length foi adicionado à posição de correspondência atual e não consegui encontrar uma maneira de contornar isso. O Regex falha em corresponder ao caso, então tomei a solução pragmática de usar apenasStringComparison.OrdinalIgnoreCase
para a minha solução.
Meu código é semelhante a outras respostas, mas minha opinião é que procuro uma correspondência antes de me dar ao trabalho de criar a StringBuilder
. Se nenhum for encontrado, uma alocação potencialmente grande será evitada. O código então se torna um do{...}while
e não umwhile{...}
Fiz alguns testes extensivos com outras respostas e isso saiu um pouco mais rápido e usou um pouco menos de memória.
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();
}