Permíteme presentar mi caso y luego puedes hacerme pedazos si quieres.
Regex no es la respuesta para este problema: demasiado lento y memoria hambrienta, en términos relativos.
StringBuilder es mucho mejor que la secuencia de cadenas.
Dado que este será un método de extensión para complementar string.Replace
, creo que es importante hacer coincidir cómo funciona, por lo tanto, lanzar excepciones para los mismos problemas de argumento es importante, ya que es devolver la cadena original si no se realizó un reemplazo.
Creo que tener un parámetro StringComparison no es una buena idea. Lo intenté pero el caso de prueba mencionado originalmente por michael-liu mostró un problema:
[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]
Si bien IndexOf coincidirá, hay una falta de coincidencia entre la longitud de la coincidencia en la cadena de origen (1) y oldValue.Length (2). Esto se manifestó al causar IndexOutOfRange en algunas otras soluciones cuando oldValue.Length se agregó a la posición de coincidencia actual y no pude encontrar una forma de evitar esto. Regex no coincide con el caso de todos modos, así que tomé la solución pragmática de usar solo StringComparison.OrdinalIgnoreCase
para mi solución.
Mi código es similar a otras respuestas, pero mi giro es que busco una coincidencia antes de tomar la molestia de crear un StringBuilder
. Si no se encuentra ninguno, se evita una asignación potencialmente grande. El código se convierte en un en do{...}while
lugar de unwhile{...}
He realizado algunas pruebas exhaustivas con otras respuestas y esto salió fraccionalmente más rápido y usó un poco menos de memoria.
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();
}