2012-02-20 13 views
93

Cadena no tiene ReplaceAt(), y estoy dando vueltas sobre cómo hacer una función decente que hace lo que necesito. Supongo que el costo de CPU es alta, pero los tamaños de cuerda son pequeñas, así que es todo muy bien¿Reemplazar un char en un índice dado en una cadena?

+4

¿No quiso decir 'does not have' en lugar de 'does have'? :) – Abbas

Respuesta

166

Utilice un StringBuilder:

StringBuilder sb = new StringBuilder(theString); 
sb[index] = newChar; 
theString = sb.ToString(); 
+3

ah, Stringbuilder es limpio – Jason94

+5

@ Jason94, no estoy seguro de que esto sea más eficiente que usar ToCharArray como en la respuesta de Jon, debe ejecutar pruebas para ver cuál es más rápido. –

+13

Probé con un pequeño punto de referencia en 100k iteraciones, ToCharArray es al menos 2 veces más rápido. –

19
string s = "ihj"; 
char[] array = s.ToCharArray(); 
array[1] = 'p'; 
s = new string(array); 
+1

Los comentarios en la respuesta aceptada indican que este enfoque es dos veces más rápido. – Dejan

69

El enfoque más simple sería algo así como:

public static string ReplaceAt(this string input, int index, char newChar) 
{ 
    if (input == null) 
    { 
     throw new ArgumentNullException("input"); 
    } 
    char[] chars = input.ToCharArray(); 
    chars[index] = newChar; 
    return new string(chars); 
} 

Esto es ahora un método de extensión por lo que puede utilizar:

var foo = "hello".ReplaceAt(2, 'x'); 
Console.WriteLine(foo); // hexlo 

sería bueno pensar en alguna manera que sólo se requiere una única copia de los datos para hacer en lugar de los dos aquí, pero no estoy seguro de ninguna manera de hacerlo. Es posible que esto lo haría:

public static string ReplaceAt(this string input, int index, char newChar) 
{ 
    if (input == null) 
    { 
     throw new ArgumentNullException("input"); 
    } 
    StringBuilder builder = new StringBuilder(input); 
    builder[index] = newChar; 
    return builder.ToString(); 
} 

... Sospecho que depende por completo de la versión del marco que está utilizando.

+2

+1 para convertirlo en un método de extensión. Como se menciona en el comentario de la respuesta aceptada y también en otras respuestas, el método ToCharArray() es más rápido que el método StringBuilder. Una mejora más posible: compruebe si ingresó.Count> index-1 && index> = 0 de lo contrario obtendrá una excepción cuando haga chars [index] o builder [index]. –

3

cadenas son objetos inmutables, por lo que no puede reemplazar un carácter determinado en la cadena. Lo que puedes hacer es crear una nueva cadena con el personaje dado reemplazado.

Pero si va a crear una nueva cadena, por qué no utilizar un StringBuilder:

string s = "abc"; 
StringBuilder sb = new StringBuilder(s); 
sb[1] = 'x'; 
string newS = sb.ToString(); 

//newS = "axc"; 
-2
public string ReplaceChar(string sourceString, char newChar, int charIndex) 
    { 
     try 
     { 
      // if the sourceString exists 
      if (!String.IsNullOrEmpty(sourceString)) 
      { 
       // verify the lenght is in range 
       if (charIndex < sourceString.Length) 
       { 
        // Get the oldChar 
        char oldChar = sourceString[charIndex]; 

        // Replace out the char ***WARNING - THIS CODE IS WRONG - it replaces ALL occurrences of oldChar in string!!!*** 
        sourceString.Replace(oldChar, newChar); 
       } 
      } 
     } 
     catch (Exception error) 
     { 
      // for debugging only 
      string err = error.ToString(); 
     } 

     // return value 
     return sourceString; 
    } 
+1

sourceString.Replace (oldChar, newChar) reemplaza todas las ocurrencias de oldChar en la cadena, no solo el carácter en charIndex. – EventHorizon

+0

cadena err = error.ToString(); ¿por qué? –

4

repente que tenía que hacer esta tarea y encontré este tema. lo tanto, este es mi variante de estilo de LINQ:

public static class Extensions 
{ 
    public static string ReplaceAt(this string value, int index, char newchar) 
    { 
     if (value.Length <= index) 
      return value; 
     else 
      return string.Concat(value.Select((c, i) => i == index ? newchar : c)); 
    } 
} 

y luego, por ejemplo:

string instr = "Replace$dollar"; 
string outstr = instr.ReplaceAt(7, ' '); 

Al final que necesitaba para utilizar Net Framework 2, por lo que utilizar una variante de clase StringBuilder aunque.

+0

¿No sería esto lento para una cadena larga? ¿Por qué hacer esto, en lugar de las soluciones 'StringBuilder' o' ToCharArray'? Es * podría * ser una copia menos de la cadena (en comparación con 'StringBuilder'), pero sospecho que muchas cosas suceden internamente. – ToolmakerSteve

0

Si su proyecto (.csproj) permite código no seguro, probablemente, esta es la solución más rápida:

namespace System 
{ 
    public static class StringExt 
    { 
    public static unsafe void ReplaceAt(this string source, int index, char value) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 

     if (index < 0 || index >= source.Length) 
      throw new IndexOutOfRangeException("invalid index value"); 

     fixed (char* ptr = source) 
     { 
      ptr[index] = value; 
     } 
    } 
    } 
} 

Usted puede usarlo como método de extensión de cadena objetos.

+1

Por supuesto, esto viola la semántica de 'string' - afecta a cualquier otra referencia a este objeto, anulando/contradiciendo la documentación de que los objetos' string' son * inmutables *. Hacerlo así, a una clase incorporada, es muy dudoso (olor a código fuerte). Necesita un comentario WARNING gigante abofeteado. Personalmente, yo ** no ** haría esto. Si alguien necesita una cadena * mutable *, entonces crea una clase apropiada, subclassing o envolviendo un 'char []'. – ToolmakerSteve

Cuestiones relacionadas