2010-05-28 8 views
12

¿Cuál es la forma más limpia de editar los caracteres en una cadena en C#?Editar caracteres en una cadena en C#

¿Cuál es el equivalente C# de esto en C++:

std::string myString = "boom"; 
myString[0] = "d"; 
+3

Dado el problema de inmutabilidad, creo que la mejor respuesta dependerá de lo que sepa sobre sus cadenas de caracteres y qué caracteres pueden cambiar y con qué frecuencia. –

+0

@ Chris Farmer: En realidad, dudo que cambie la respuesta mucho. Cualquier implementación razonablemente eficiente usará una matriz char o StringBuilder. Cuál es el "mejor" es principalmente una cuestión de gusto. – hemp

+0

@hemp: no estoy en desacuerdo. Había escrito mi comentario antes de ver la sugerencia de StringBuilder. Creo que es una excelente forma de manejar casi todos estos casos. –

Respuesta

15

Decidido a qué hora me sentí donde los dos enfoques más canónicos, más uno tiré como no representados; esto es lo que encontré (versión de lanzamiento):

ReplaceAtChars: 86ms 
ReplaceAtSubstring: 258ms 
ReplaceAtStringBuilder: 161ms

Es evidente que el enfoque de matriz de caracteres es, con mucho, la mejor optimizado el tiempo de ejecución. Lo que en realidad sugiere que la respuesta actual de (StringBuilder) probablemente no sea la mejor respuesta.

Y aquí era la prueba utilicé:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("ReplaceAtChars: " + new Stopwatch().Time(() => "test".ReplaceAtChars(1, 'E').ReplaceAtChars(3, 'T'), 1000000) + "ms"); 
     Console.WriteLine("ReplaceAtSubstring: " + new Stopwatch().Time(() => "test".ReplaceAtSubstring(1, 'E').ReplaceAtSubstring(3, 'T'), 1000000) + "ms"); 
     Console.WriteLine("ReplaceAtStringBuilder: " + new Stopwatch().Time(() => "test".ReplaceAtStringBuilder(1, 'E').ReplaceAtStringBuilder(3, 'T'), 1000000) + "ms"); 
    } 
} 

public static class ReplaceAtExtensions 
{ 
    public static string ReplaceAtChars(this string source, int index, char replacement) 
    { 
     var temp = source.ToCharArray(); 
     temp[index] = replacement; 
     return new String(temp); 
    } 

    public static string ReplaceAtStringBuilder(this string source, int index, char replacement) 
    { 
     var sb = new StringBuilder(source); 
     sb[index] = replacement; 
     return sb.ToString(); 
    } 

    public static string ReplaceAtSubstring(this string source, int index, char replacement) 
    { 
     return source.Substring(0, index) + replacement + source.Substring(index + 1); 
    } 
} 

public static class StopwatchExtensions 
{ 
    public static long Time(this Stopwatch sw, Action action, int iterations) 
    { 
     sw.Reset(); 
     sw.Start(); 
     for (int i = 0; i < iterations; i++) 
     { 
      action(); 
     } 
     sw.Stop(); 

     return sw.ElapsedMilliseconds; 
    } 
} 
+0

+1 Para una buena respuesta con razonamiento bien respaldado –

8

cadenas en .Net son inmutables. Eso significa que usted no puede editarlos. Todo lo que puedes hacer es crear nuevos. Usted tiene que hacer algo como esto:

string myString = "boom"; 
myString = "d" + myString.Substring(1); 
+2

esto funciona pero se volvería tedioso rápidamente si tuviera que editar varios caracteres en cadenas más largas. – Tesserex

8

Las cadenas son inmutables, por lo que no puede simplemente cambiarlas.

Puede obtener un char [] de la cadena, realizar los cambios y crear una nueva cadena a partir de la matriz alterada.

Puede usar cosas como replace, aunque no le dan el control sobre la base de caracteres como C. También crearán una nueva cadena con cada cambio, donde el estilo char [] le permite realizar todos los cambios y luego crear una nueva cadena a partir de él.

30

Use un StringBuilder en su lugar.

string is immutable as described by MSDN:

cadenas son inmutables - el contenido de un objeto de cadena no se puede cambiar después de crear el objeto, aunque la sintaxis hace que parezca como si puede hacer esto.

Así que quieres algo como:

StringBuilder sb = new StringBuilder("Bello World!"); 
sb[0] = 'H'; 
string str = sb.ToString(); 
+2

+1 Eso está limpio –

+2

Recibí mi voto positivo porque es la única otra respuesta que ofrece una manera flexible de hacer realmente lo que quiere, correctamente, sin costoso o más construcciones elaboradas. – Tesserex

+0

@tesserex - No niego que esto sea flexible, pero difícilmente consideraría el mío caro o demasiado elaborado. De hecho, es idéntica a la mía, excepto que usa una construcción más elaborada. – corsiKa

5

No es posible ya que las cadenas son inmutables.

siempre se puede rodar su propio método de extensión para hacer algo similar:

public static string ReplaceAtIndex(this string original, 
    int index, char character) 
{ 
    char[] temp = original.ToCharArray(); 
    temp[index] = character; 
    return new String(temp); 
} 

y llamarlo como:

string newString = myString.ReplaceAtIndex(0, 'd'); 
+2

Cada llamada a esa función crearía 3 cadenas temporales. Eso no es terrible, pero si necesitaras hacer esto un montón de veces, sería bastante caro. – hemp

+0

@hemp - definitivamente nunca dijo que era la mejor implementación. heh. Voy a actualizar con algo un poco mejor. –

+0

+1 Buena respuesta: ¿cuál es la sobrecarga como la construcción que has usado allí llamada C#? –

3
string myString = "boom"; 
char[] arr = myString.ToCharArray(); 
arr[0] = 'd'; 
myString = new String(arr); 
+0

Como muestra mi prueba de tiempo, este algoritmo en realidad funciona mejor que StringBuilder. – hemp

3
string myString = "boom"; 
char[] myChars = myString.ToCharArray(); 
myChars[0] = 'd'; 
myString = new String(myChars); 
+1

no puede simplemente llamar a ToString() en una matriz de caracteres. No está sobrecargado para convertir a una cadena. Simplemente te da "System.Char []" – Tesserex

+0

Sí, tienes razón, eso me atrapa todo el tiempo. He editado apropiadamente. Gracias. –

4

Como dijo Brian, utilice un StringBuilder, en su lugar:

string myString = "boom"; 
StringBuilder myStringBuilder = new StringBuilder(myString); 
myStringBuilder[0] = 'd'; 
// Whatever else you're going to do to it 

Siempre que necesite la cadena de nuevo, llamar myStringBuilder.ToString()

+0

Sí, probablemente vaya con esto –

2

Aquí es una diversión que arme. Ahora, tenga en cuenta que esto no es muy eficiente, especialmente para reemplazos simples. Sin embargo, fue divertido escribir y se presta a un patrón de uso bastante legible. También resalta el hecho poco conocido de que String implementa IEnumerable.

public static class LinqToStrings 
{ 
    public static IQueryable<char> LinqReplace(this string source, int startIndex, int length, string replacement) 
    { 
     var querySource = source.AsQueryable(); 
     return querySource.LinqReplace(startIndex, length, replacement); 
    } 

    public static IQueryable<char> LinqReplace(this IQueryable<char> source, int startIndex, int length, string replacement) 
    { 
     var querySource = source.AsQueryable(); 
     return querySource.Take(startIndex).Concat(replacement).Concat(querySource.Skip(startIndex + length)); 
    } 

    public static string AsString(this IQueryable<char> source) 
    { 
     return new string(source.ToArray()); 
    } 
} 

Y éstas son algunas ejemplo de uso:

public void test() 
{ 
    var test = "test"; 
    Console.WriteLine("Old: " + test); 
    Console.WriteLine("New: " + test.LinqReplace(0, 4, "SOMEPIG") 
            .LinqReplace(4, 0, "terrific") 
            .AsString()); 
} 

Salidas:

Old: test 
New: SOMEterrificPIG

Otra versión de este mismo enfoque, que no es tan terriblemente lento es sencillo usando Subcadena:

public static string ReplaceAt(this string source, int startIndex, int length, string replacement) 
{ 
    return source.Substring(0, startIndex) + replacement + source.Substring(startIndex + length); 
} 

Y en un maravilloso ejemplo de por qué usted debe perfila tu código y por qué probablemente debería no usar mi aplicación LinqToStrings en el código de producción, he aquí una prueba de tiempo:

Console.WriteLine("Using LinqToStrings: " + new Stopwatch().Time(() => "test".LinqReplace(0, 4, "SOMEPIG").LinqReplace(4, 0, "terrific").AsString(), 1000)); 
Console.WriteLine("Using Substrings: " + new Stopwatch().Time(() => "test".ReplaceAt(0, 4, "SOMEPIG").ReplaceAt(4, 0, "terrific"), 1000)); 

Qué mide pasos de temporizador en 1.000 iteraciones, la producción de esta salida:

 
Using LinqToStrings: 3,818,953 
Using Substrings: 1,157 
+0

Es genial ... pero en realidad no soluciona el problema. Él quiere modificar, no insertar. Aún así, me gusta el enfoque, y tiene razón acerca de que tanto el patrón es bueno, y el rendimiento es ... ¡no tan bueno! – corsiKa

+0

En realidad, soluciona el problema. Como toma un índice y una longitud de inicio, se puede usar para insertar, quitar o reemplazar. Mi ejemplo en realidad muestra reemplazar e insertar ("prueba" se convierte en "SOMEPIG", que luego se convierte en "SOMEterrificPIG".) Dicho eso, no es un enfoque que recomiendo a menos que estés aburrido. – hemp

2

cadenas son inmutables en C#. Use la clase StringBuilder o una matriz de caracteres.

StringBuilder sb = new StringBuilder("boom"); 
sb[0] = 'd'; 

IIRC, .NET utiliza lo que se llama String Pooling. Cada vez que se crea un nuevo literal de cadena, se almacena en la memoria como parte del grupo de cadenas. Si crea una segunda cadena que coincida con una cadena en el grupo de cadenas, ambas variables harán referencia a la misma memoria.

Cuando intenta hacer una operación como la que hizo para reemplazar el carácter 'b' con un carácter 'd' usando cadenas en .NET, su programa está creando una segunda cadena en el grupo de cadenas con el valor "fatalidad ", aunque desde su perspectiva, no parece que esto esté sucediendo en absoluto. Al leer el código, uno supondría que el personaje está siendo reemplazado.

Lo mencioné porque me encuentro con esta pregunta todo el tiempo, y la gente a menudo pregunta por qué deberían usar StringBuilder cuando una cadena puede hacer lo mismo. Bueno, técnicamente no puede, pero está diseñado de forma que parezca como si fuera posible.

+0

Gracias por esa información. - ¿Se crean cadenas StringBuilder en el grupo de cadenas? –

Cuestiones relacionadas