¿Cómo reemplazar parte de la cuerda por posición?

155

Tengo esta cadena: ABCDEFGHIJ

Necesito reemplazar de la posición 4 a la posición 5 con la cuerda ZX

Se verá así: ABCZXFGHIJ

Pero no usar con string.replace("DE","ZX")- Necesito usar con posición

¿Cómo puedo hacerlo?

Gali
fuente
@TotZam - por favor verifique las fechas. Este es más antiguo que el que vinculaste.
ToolmakerSteve
@ToolmakerSteve Generalmente miro la calidad de la pregunta y las respuestas, no las fechas, como se dice aquí . En este caso, parece que he cometido un error y he hecho clic en el incorrecto para marcarlo como duplicado, ya que la calidad de esta pregunta es obviamente mejor, por lo que he marcado la otra pregunta.
Tot Zam
@TotZam - ah, no sabía sobre esa recomendación - gracias por señalarlo. (Aunque es confuso ver que algo antiguo se informa como un duplicado de algo más nuevo, por lo que en tal caso, valdría la pena explicar explícitamente que está marcando como duplicado una pregunta ANTERIOR, porque la vinculada tiene mejores respuestas).
ToolmakerSteve

Respuestas:

198

La forma más fácil de agregar y eliminar rangos en una cadena es usar el StringBuilder.

var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();

Una alternativa es usarla String.Substring, pero creo que el StringBuildercódigo se vuelve más legible.

Albin Sunnanbo
fuente
66
sería mejor concluir eso en un método de extensión :)
balexandre
¿Por qué usas un StringBuilder aquí? Verifique la respuesta de V4Vendetta, que es mucho más legible ...
Fortega
1
@Fortega Remove () e Insert () crean y devuelven una nueva cadena. El enfoque StringBuilder evita este costo adicional trabajando dentro de una sección de memoria preasignada y moviendo caracteres dentro de ella.
redcalx
@redcalx Eso es cierto, pero se introduce un costo adicional al introducir un objeto StringBuilder y al reducir la legibilidad
Fortega
2
¿Cuál es el punto de usar StringBuilder aquí, si está llamando Eliminar e Insertar de la misma manera que V4Vendetta, pero lo está haciendo directamente en la cadena? Parece código redundante.
metabuddy
186
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
V4Vendetta
fuente
17
Prefiero esto sobre inicializar un nuevo StringBuilder (...). ¡Gracias!
Michael Norgren
3
No sé por qué alguien usaría stringbuilder para esta operación. Esta es una buena respuesta
NappingRabbit
43

ReplaceAt (int index, int length, string replace)

Aquí hay un método de extensión que no usa StringBuilder o Substring. Este método también permite que la cadena de reemplazo se extienda más allá de la longitud de la cadena de origen.

//// str - the source string
//// index- the start location to replace at (0-based)
//// length - the number of characters to be removed before inserting
//// replace - the string that is replacing characters
public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return str.Remove(index, Math.Min(length, str.Length - index))
            .Insert(index, replace);
}

Cuando utilice esta función, si desea que toda la cadena de reemplazo reemplace tantos caracteres como sea posible, configure la longitud a la longitud de la cadena de reemplazo:

"0123456789".ReplaceAt(7, 5, "Hello") = "0123456Hello"

De lo contrario, puede especificar la cantidad de caracteres que se eliminarán:

"0123456789".ReplaceAt(2, 2, "Hello") = "01Hello456789"

Si especifica que la longitud sea 0, esta función actúa igual que la función de inserción:

"0123456789".ReplaceAt(4, 0, "Hello") = "0123Hello456789"

Supongo que esto es más eficiente ya que la clase StringBuilder no necesita inicializarse y ya que usa operaciones más básicas. Por favor, corríjame si estoy equivocado. :)

Nicholas Miller
fuente
55
Stringbuilder es más eficiente si la cadena es más larga que, digamos 200 caracteres.
Tim Schmelter
Sin estrés. Trabajado derecho. Gracias
D_Edet
9

Use String.Substring()(detalles aquí ) para cortar la parte izquierda, luego su reemplazo, luego la parte derecha. Juega con índices hasta que lo hagas bien :)

Algo como:

string replacement=original.Substring(0,start)+
    rep+original.Substring(start+rep.Length);
Daniel Mošmondor
fuente
2
Creé tres métodos usando los tres métodos anteriores (string.Remove / Insert, Stringbuilder.Remove / Insert y Daniel's Substring answer, y SubString fue el método más rápido.
Francine DeGrood Taylor
7

Como un método de extensión.

public static class StringBuilderExtension
{
    public static string SubsituteString(this string OriginalStr, int index, int length, string SubsituteStr)
    {
        return new StringBuilder(OriginalStr).Remove(index, length).Insert(index, SubsituteStr).ToString();
    }
}
Ali Khalid
fuente
5
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);
Javed Akram
fuente
Esto funciona, probablemente no quieras volver a ponerlo de tnuevo.
Pierre
4

Si le importa el rendimiento, lo que quiere evitar aquí son las asignaciones. Y si está utilizando .Net Core 2.1+ (o, aún no publicado, .Net Standard 2.1), puede hacerlo utilizando el string.Createmétodo :

public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return string.Create(str.Length - length + replace.Length, (str, index, length, replace),
        (span, state) =>
        {
            state.str.AsSpan().Slice(0, state.index).CopyTo(span);
            state.replace.AsSpan().CopyTo(span.Slice(state.index));
            state.str.AsSpan().Slice(state.index + state.length).CopyTo(span.Slice(state.index + state.replace.Length));
        });
}

Este enfoque es más difícil de entender que las alternativas, pero es el único que asignará solo un objeto por llamada: la cadena recién creada.

svick
fuente
3

Podría intentar algo enlazar esto:

string str = "ABCDEFGHIJ";
str = str.Substring(0, 2) + "ZX" + str.Substring(5);
MBU
fuente
3

Como otros han mencionado, la Substring()función está ahí por una razón:

static void Main(string[] args)
{
    string input = "ABCDEFGHIJ";

    string output = input.Overwrite(3, "ZX"); // 4th position has index 3
    // ABCZXFGHIJ
}

public static string Overwrite(this string text, int position, string new_text)
{
    return text.Substring(0, position) + new_text + text.Substring(position + new_text.Length);
}

También calculé esto contra la StringBuildersolución y obtuve 900 tics frente a 875. Por lo tanto, es un poco más lento.

John Alexiou
fuente
@ Aaron.S - no, no lo hace. En mi ejemplo "ABCDEFGHIJ"y "ZX"son de diferentes longitudes.
John Alexiou
3

Aún otra

    public static string ReplaceAtPosition(this string self, int position, string newValue)        
    {
        return self.Remove(position, newValue.Length).Insert(position, newValue); 
    }
Stijn Van Antwerpen
fuente
2

Con la ayuda de esta publicación, creo la siguiente función con verificaciones de longitud adicionales

public string ReplaceStringByIndex(string original, string replaceWith, int replaceIndex)
{
    if (original.Length >= (replaceIndex + replaceWith.Length))
    {
        StringBuilder rev = new StringBuilder(original);
        rev.Remove(replaceIndex, replaceWith.Length);
        rev.Insert(replaceIndex, replaceWith);
        return rev.ToString();
    }
    else
    {
        throw new Exception("Wrong lengths for the operation");
    }
}
Hassan
fuente
2

Todas las demás respuestas no funcionan si la cadena contiene caracteres Unicode (como Emojis) porque un carácter Unicode pesa más bytes que un carácter.

Ejemplo : el emoji '🎶' convertido a bytes, pesará el equivalente a 2 caracteres. Entonces, si el carácter unicode se coloca al comienzo de su cadena, el offsetparámetro se desplazará).

Con este tema , extiendo la clase StringInfo a Reemplazar por posición manteniendo el algoritmo de Nick Miller para evitar eso:

public static class StringInfoUtils
{
    public static string ReplaceByPosition(this string str, string replaceBy, int offset, int count)
    {
        return new StringInfo(str).ReplaceByPosition(replaceBy, offset, count).String;
    }

    public static StringInfo ReplaceByPosition(this StringInfo str, string replaceBy, int offset, int count)
    {
        return str.RemoveByTextElements(offset, count).InsertByTextElements(offset, replaceBy);
    }

    public static StringInfo RemoveByTextElements(this StringInfo str, int offset, int count)
    {
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            offset + count < str.LengthInTextElements
                ? str.SubstringByTextElements(offset + count, str.LengthInTextElements - count - offset)
                : ""
            ));
    }
    public static StringInfo InsertByTextElements(this StringInfo str, int offset, string insertStr)
    {
        if (string.IsNullOrEmpty(str?.String))
            return new StringInfo(insertStr);
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            insertStr,
            str.LengthInTextElements - offset > 0 ? str.SubstringByTextElements(offset, str.LengthInTextElements - offset) : ""
        ));
    }
}
GGO
fuente
dotnetfiddle.net/l0dqS5 No pude hacer que otros métodos fallaran en Unicode. Modifique el violín para demostrar su caso de uso. Muy interesado, en resultados.
Markus
1
@ MarkusHooge Intente colocar los caracteres unicode al comienzo de su cadena como: dotnetfiddle.net/3V2K3Y . Verá que solo la última línea funciona bien y colocará el carácter en el cuarto lugar (en el otro caso, el carácter unicode tiene una longitud de 2 caracteres)
GGO
Tienes razón, no está claro. Actualicé mi respuesta para agregar más explicaciones de por qué la respuesta de los demás no funciona
GGO
1

Estaba buscando una solución con los siguientes requisitos:

  1. use solo una expresión de una línea
  2. use solo métodos incorporados al sistema (ninguna utilidad implementada personalizada)

Solución 1

La solución que mejor me conviene es esta:

// replace `oldString[i]` with `c`
string newString = new StringBuilder(oldString).Replace(oldString[i], c, i, 1).ToString();

Esto usa StringBuilder.Replace(oldChar, newChar, position, count)

Solución 2

La otra solución que satisface mis requisitos es usar Substring con concatenación:

string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);

Esto también está bien. me conjetura que no es tan eficiente como la primera actuación sabia (debido a la concatenación de cadenas innecesario). Pero la optimización prematura es la raíz de todo mal .

Así que elige el que más te guste :)

KFL
fuente
La solución 2 funciona pero requiere corrección: string newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
Yura G
0

Es mejor usar el String.substr() .

Me gusta esto:

ReplString = GivenStr.substr(0, PostostarRelStr)
           + GivenStr(PostostarRelStr, ReplString.lenght());
Varun Kumar
fuente
0
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();

Déjame explicarte mi solución.
Dado el enunciado del problema de alterar una cadena en sus dos posiciones específicas (“posición 4 a posición 5”) con dos caracteres 'Z' y 'X' y la pregunta es usar el índice de posición para alterar la cadena y no la cadena Reemplazar ( ) método (puede deberse a la posibilidad de repetición de algunos caracteres en la cadena real), preferiría utilizar un enfoque minimalista para lograr el objetivo sobre el uso de Substring()y cadena Concat()o cadena Remove()y Insert()enfoque. Aunque todas esas soluciones servirán para lograr el mismo objetivo, pero solo depende de la elección personal y la filosofía de resolver con un enfoque minimalista.
Volviendo a la solución mencionada anteriormente, si miramos más de cercastring yStringBuilder, ambos tratan internamente una cadena dada como una matriz de caracteres. Si observamos su implementación StringBuilder, mantiene una variable interna algo así como " internal char[] m_ChunkChars;" para capturar la cadena dada. Ahora, dado que esta es una variable interna, no podemos acceder directamente a la misma. Para el mundo externo, para poder acceder y modificar esa matriz de caracteres, los StringBuilderexpone a través de la propiedad del indexador que se parece a continuación

    [IndexerName("Chars")]
    public char this[int index]
    {
      get
      {
        StringBuilder stringBuilder = this;
        do
        {
          // … some code
            return stringBuilder.m_ChunkChars[index1];
          // … some more code
        }
      }
      set
      {
        StringBuilder stringBuilder = this;
        do
        {
            //… some code
            stringBuilder.m_ChunkChars[index1] = value;
            return;
            // …. Some more code
        }
      }
    }

Mi solución mencionada anteriormente aprovecha esta capacidad de indexador para alterar directamente la matriz de caracteres mantenida internamente, que IMO es eficiente y minimalista.

Por cierto; podemos reescribir la solución anterior de manera más elaborada, algo como a continuación

 string myString = "ABCDEFGHIJ";
 StringBuilder tempString = new StringBuilder(myString);
 tempString[3] = 'Z';
 tempString[4] = 'X';
 string modifiedString = tempString.ToString();

En este contexto, también me gustaría mencionar que en caso de stringque también tenga una propiedad de indexador como un medio para exponer su matriz de caracteres interna, pero en este caso solo tiene la propiedad Getter (y no Setter) ya que la cadena es inmutable por naturaleza. Y es por eso que necesitamos usar StringBuilderpara alterar la matriz de caracteres.

[IndexerName("Chars")]
public extern char this[int index] { [SecuritySafeCritical, __DynamicallyInvokable, MethodImpl(MethodImplOptions.InternalCall)] get; }

Y por último, pero no menos importante, esta solución solo se ajusta mejor a este problema específico en el que la solicitud es reemplazar solo unos pocos caracteres con un índice de posición conocido por adelantado. Es posible que no sea la mejor opción cuando el requisito es alterar una cadena bastante larga, es decir, el número de caracteres a modificar es grande en números.

Como si
fuente
1
Edite su respuesta para explicar cómo este código responde la pregunta
tshimkus
0
String timestamp = "2019-09-18 21.42.05.000705";
String sub1 = timestamp.substring(0, 19).replace('.', ':'); 
String sub2 = timestamp.substring(19, timestamp.length());
System.out.println("Original String "+ timestamp);      
System.out.println("Replaced Value "+ sub1+sub2);
usuario1355816
fuente
0

Aquí hay un método de extensión simple:

    public static class StringBuilderExtensions
    {
        public static StringBuilder Replace(this StringBuilder sb, int position, string newString)
            => sb.Replace(position, newString.Length, newString);

        public static StringBuilder Replace(this StringBuilder sb, int position, int length, string newString)
            => (newString.Length <= length)
                ? sb.Remove(position, newString.Length).Insert(position, newString)
                : sb.Remove(position, length).Insert(position, newString.Substring(0, length));
    }

Úselo así:

var theString = new string(' ', 10);
var sb = new StringBuilder(theString);
sb.Replace(5, "foo");
return sb.ToString();
MovGP0
fuente
-2

Creo que la forma más simple sería esta: (sin stringbuilder)

string myString = "ABCDEFGHIJ";
char[] replacementChars = {'Z', 'X'};
byte j = 0;

for (byte i = 3; i <= 4; i++, j++)  
{                   
myString = myString.Replace(myString[i], replacementChars[j]);  
}

Esto funciona porque una variable de tipo cadena se puede tratar como una matriz de variables char. Puede, por ejemplo, referirse al segundo carácter de una variable de cadena con el nombre "myString" como myString [1]

Sfou
fuente
Esto solo funciona porque, en el ejemplo del autor, stringlos caracteres a ser reemplazados aparecen una sola vez. Si ejecuta esto contra, digamos, "DBCDEFGHIE"entonces obtiene "ZBCZXFGHIX", que no es el resultado deseado. Además, no estaría de acuerdo en que esto sea más simple que las otras StringBuildersoluciones que se publicaron hace dos años y medio.
BACON