2009-08-24 6 views
56

¿Hay una mejor manera de hacer esto ...sustituir los elementos de cadena múltiple en C#

MyString.Trim().Replace("&", "and").Replace(",", "").Replace(" ", " ") 
     .Replace(" ", "-").Replace("'", "").Replace("/", "").ToLower(); 

He ampliado la clase cadena para mantenerla baja a un puesto de trabajo, pero hay una manera más rápida?

public static class StringExtension 
{ 
    public static string clean(this string s) 
    { 
     return s.Replace("&", "and").Replace(",", "").Replace(" ", " ") 
       .Replace(" ", "-").Replace("'", "").Replace(".", "") 
       .Replace("eacute;", "é").ToLower(); 
    } 
} 

Sólo por diversión (y para detener los argumentos en los comentarios) He empujó una esencia a la evaluación comparativa de los diversos ejemplos a continuación.

https://gist.github.com/ChrisMcKee/5937656

La opción de expresiones regulares puntuaciones terriblemente; la opción del diccionario aparece más rápido; la versión larga de la sustitución de stringbuilder es ligeramente más rápida que la mano corta.

+0

Sobre la base de lo que tiene en sus puntos de referencia que se parece a la versión del diccionario no está haciendo todos los reemplazos y sospecho que es lo que está haciendo más rápido de lo las soluciones StringBuilder. – toad

+1

@toad Hola desde 2009; Agregué un comentario más abajo en abril sobre ese flagrante error. Aunque la esencia se actualizó, omití D. La versión del diccionario es aún más rápida. –

+0

Posible duplicado de [Alternativa a String.Replace multiple times?] (Http://stackoverflow.com/questions/12007358/alternative-to-string-replace-multiple-times) –

Respuesta

79

Más rápido - no. Más efectivo: sí, si usará la clase StringBuilder. Con su implementación, cada operación genera una copia de una cadena que bajo ciertas circunstancias puede afectar el rendimiento. Las cadenas son objetos inmutables por lo que cada operación solo devuelve una copia modificada.

Si espera que este método sea llamado activamente en múltiples Strings de longitud significativa, podría ser mejor "migrar" su implementación a la clase StringBuilder. Con él, cualquier modificación se realiza directamente en esa instancia, por lo que ahorra operaciones de copia innecesarias.

public static class StringExtention 
{ 
    public static string clean(this string s) 
    { 
     StringBuilder sb = new StringBuilder (s); 

     sb.Replace("&", "and"); 
     sb.Replace(",", ""); 
     sb.Replace(" ", " "); 
     sb.Replace(" ", "-"); 
     sb.Replace("'", ""); 
     sb.Replace(".", ""); 
     sb.Replace("eacute;", "é"); 

     return sb.ToString().ToLower(); 
    } 
} 
+1

Para mayor claridad, la respuesta del diccionario es la más rápida http://stackoverflow.com/a/1321366/52912 –

+2

En su punto de referencia en https://gist.github.com/ChrisMcKee/5937656 la prueba del diccionario no está completo: no hace todos los reemplazos y "" reemplaza "", no "". No hacer todos los reemplazos podría ser la razón, por qué es más rápido en el punto de referencia. El recambio de regex no está completo, tampoco. Pero lo más importante es que su cadena TestData es muy corta. Al igual que los estados de respuesta aceptados, la cadena debe ser de una longitud significativa para que StringBuilder sea una ventaja. ¿Podría repetir el punto de referencia con cadenas de 10kB, 100kB y 1MB? – Leif

+0

Es un buen punto; tal como está, se estaba utilizando para la limpieza de URL, por lo que las pruebas a 100 kb - 1 mb no habrían sido realistas. Actualizaré el punto de referencia por lo que está usando todo, fue un error. –

8

esto será más eficiente:

public static class StringExtension 
{ 
    public static string clean(this string s) 
    { 
     return new StringBuilder(s) 
       .Replace("&", "and") 
       .Replace(",", "") 
       .Replace(" ", " ") 
       .Replace(" ", "-") 
       .Replace("'", "") 
       .Replace(".", "") 
       .Replace("eacute;", "é") 
       .ToString() 
       .ToLower(); 
    } 
} 
+0

Realmente difícil de leer. Estoy seguro de que sabes lo que hace pero un Junior Dev se rascará la cabeza por lo que realmente sucede. Estoy de acuerdo, también siempre busco la mano corta de escribir algo, pero fue solo para mi propia satisfacción. Otras personas estaban volviendo locas en la pila de desastre. – ppumkin

+2

Esto es realmente más lento. BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms Varía en las repeticiones, pero la respuesta gana https://gist.github.com/anonymous/5937596 –

10

Tal vez un poco más legible?

public static class StringExtension { 

     private static Dictionary<string, string> _replacements = new Dictionary<string, string>(); 

     static StringExtension() { 
      _replacements["&"] = "and"; 
      _replacements[","] = ""; 
      _replacements[" "] = " "; 
      // etc... 
     } 

     public static string clean(this string s) { 
      foreach (string to_replace in _replacements.Keys) { 
       s = s.Replace(to_replace, _replacements[to_replace]); 
      } 
      return s; 
     } 
    } 

También añadir nuevo en la sugerencia de la ciudad sobre StringBuilder ...

+4

sería más legible así: 'private static Dictionary _replacements = new Dictionary () {{" & "," and "}, {", "," "}, {" "," " }/* etc * /}; ' – ANeves

+1

o por supuesto ... private static readonly Dictionary Replacements = new Dictionary () {{" & "," and "}, { ",", ""}, {"", ""}/* etc * /}; \t \t public static string Limpio (esta cadena s) \t \t { \t \t \t retorno Replacements.Keys.Aggregate (s, (corriente, toreplace) => current.Replace (toreplace, reemplazos [toreplace])); \t –

1

que estoy haciendo algo similar, pero en mi caso estoy haciendo serialización/De-serialización así que tengo que ser capaz de ir ambas direcciones. Creo que el uso de una cadena [] [] funciona de forma casi idéntica al diccionario, incluida la inicialización, pero también puede ir en la otra dirección, devolviendo los sustitutos a sus valores originales, algo que el diccionario realmente no está configurado para hacer.

Editar: Puede utilizar Dictionary<Key,List<Values>> el fin de obtener mismo resultado que la cadena [] []

4

Si usted es simplemente después de una solución bastante y no es necesario para ahorrar unos cuantos nanosegundos, ¿qué tal un poco de azúcar LINQ ?

var input = "test1test2test3"; 
var replacements = new Dictionary<string, string> { { "1", "*" }, { "2", "_" }, { "3", "&" } }; 

var output = replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value)); 
+0

Similar al ejemplo C en el Gist (si miras arriba, la declaración del linq es más fea en el comentario) –

+1

Es interesante que definas una declaración funcional como "Uglier" más que como una de procedimiento. – TimS

+0

no voy a discutir al respecto; es meramente preferencia. Como dices, linq es simplemente azúcar sintáctica; y como ya dije, ya coloqué el equivalente arriba del código :) –

3

Hay una cosa que se puede optimizar en las soluciones sugeridas. Tener muchas llamadas al Replace() hace que el código haga múltiples pasadas sobre la misma cadena. Con cadenas muy largas, las soluciones pueden ser lentas debido a fallas en la capacidad de caché de la CPU. Puede ser uno debería considerar replacing multiple strings in a single pass.

-1
string input = "it's worth a lot of money, if you can find a buyer."; 
for (dynamic i = 0, repl = new string[,] { { "'", "''" }, { "money", "$" }, { "find", "locate" } }; i < repl.Length/2; i++) { 
    input = input.Replace(repl[i, 0], repl[i, 1]); 
} 
+1

Debería considerar agregar contexto a sus respuestas. Como una breve explicación de lo que está haciendo Y, si es relevante, por qué lo escribió de la manera en que lo hizo. – Neil

1

Otra opción es el uso de LINQ

[TestMethod] 
public void Test() 
{ 
    var input = "it's worth a lot of money, if you can find a buyer."; 
    var expected = "its worth a lot of money if you can find a buyer"; 
    var removeList = new string[] { ".", ",", "'" }; 
    var result = input; 

    removeList.ToList().ForEach(o => result = result.Replace(o, string.Empty)); 

    Assert.AreEqual(expected, result); 
} 
+0

Puede declarar 'var removeList = new List {/*...*/};' luego simplemente llame a 'removeList.ForEach (/*...*/);' y simplifique su código. Tenga en cuenta también que no responde completamente la pregunta porque * all * found strings se reemplazan por 'String.Empty'. –

Cuestiones relacionadas