Come sostituire parte della stringa per posizione?


155

Ho questa stringa: ABCDEFGHIJ

Devo sostituire dalla posizione 4 alla posizione 5 con la stringa ZX

Sarà simile a questo: ABCZXFGHIJ

Ma non usare con string.replace("DE","ZX")- Devo usare con position

Come posso farlo?


@TotZam - controlla le date. Questo è più vecchio di quello che hai collegato.
ToolmakerSteve

@ToolmakerSteve Di solito guardo la qualità della domanda e delle risposte, non le date, come si dice fare qui . In questo caso, mi sembra di aver fatto un errore e di aver cliccato su quello sbagliato per contrassegnarlo come duplicato, poiché la qualità di questa domanda è ovviamente migliore e quindi ho segnalato l'altra domanda.
Tot Zam,

@TotZam - ah, non sapevo di quella raccomandazione - grazie per averlo sottolineato. (Anche se è confuso vedere qualcosa di più vecchio riportato come duplicato di qualcosa di più nuovo, quindi in tal caso, varrebbe la pena spiegare esplicitamente che stai contrassegnando come duplicato una domanda OLDER, perché quella collegata ha risposte migliori.)
ToolmakerSteve

Risposte:


198

Il modo più semplice per aggiungere e rimuovere intervalli in una stringa è usare il StringBuilder.

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

Un'alternativa è usare String.Substring, ma penso che il StringBuildercodice diventi più leggibile.


6
sarebbe meglio racchiuderlo in un metodo di estensione :)
balexandre,

Perché usi StringBuilder qui? Controlla la risposta di V4Vendetta, che è molto più leggibile ...
Fortega,

1
@Fortega Remove () e Insert () creano e restituiscono ciascuno una nuova stringa. L'approccio StringBuilder evita questo costo aggiuntivo lavorando all'interno di una sezione di memoria pre-allocata e spostando i caratteri al suo interno.
redcalx,

@redcalx È vero, ma viene introdotto un costo aggiuntivo introducendo un oggetto StringBuilder e riducendo la leggibilità
Fortega,

2
Che senso ha usare StringBuilder qui, se stai chiamando Rimuovi e Inserisci allo stesso modo di V4Vendetta, ma lo sta facendo direttamente sulla stringa? Sembra un codice ridondante.
metabuddy,

186
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");

17
Preferisco questo rispetto all'inizializzazione di un nuovo StringBuilder (...). Grazie!
Michael Norgren,

3
Non so perché qualcuno dovrebbe usare Stringbuilder per questa operazione. Questa è una buona risposta
NappingRabbit

43

ReplaceAt (indice int, lunghezza int, sostituzione stringa)

Ecco un metodo di estensione che non utilizza StringBuilder o Substring. Questo metodo consente inoltre alla stringa di sostituzione di estendersi oltre la lunghezza della stringa di origine.

//// 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);
}

Quando si utilizza questa funzione, se si desidera che l'intera stringa di sostituzione sostituisca il maggior numero possibile di caratteri, impostare la lunghezza sulla lunghezza della stringa di sostituzione:

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

Altrimenti, puoi specificare la quantità di caratteri che verranno rimossi:

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

Se si specifica che la lunghezza è 0, questa funzione si comporta proprio come la funzione di inserimento:

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

Immagino che questo sia più efficiente poiché non è necessario inizializzare la classe StringBuilder e poiché utilizza più operazioni di base. Perfavore, correggimi se sbaglio. :)


5
Stringbuilder è più efficiente se la stringa è più lunga di, diciamo 200 caratteri.
Tim Schmelter,

Niente stress. Ha lavorato dritto. Grazie
D_Edet il

9

Utilizzare String.Substring()(dettagli qui ) per tagliare la parte sinistra, quindi la sostituzione, quindi la parte destra. Gioca con gli indici fino a quando non hai capito bene :)

Qualcosa di simile a:

string replacement=original.Substring(0,start)+
    rep+original.Substring(start+rep.Length);

2
Ho creato tre metodi usando i tre metodi precedenti (string.Remove / Insert, Stringbuilder.Remove / Insert e la risposta di Substring di Daniel, e SubString era il metodo più veloce.
Francine DeGrood Taylor,

7

Come metodo di estensione.

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();
    }
}

5
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);

Funziona, probabilmente non vorrai rimetterlo di tnuovo.
Pierre,

4

Se ti preoccupi delle prestazioni, la cosa che vuoi evitare qui sono le allocazioni. E se utilizzi .Net Core 2.1+ (o lo standard .Net 2.1 inedito), puoi farlo utilizzando il string.Createmetodo :

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));
        });
}

Questo approccio è più difficile da comprendere rispetto alle alternative, ma è l'unico che assegnerà un solo oggetto per chiamata: la stringa appena creata.


3

Puoi provare qualcosa che collega questo:

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

3

Come altri hanno già detto, la Substring()funzione è lì per un motivo:

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);
}

Inoltre ho cronometrato questo contro la StringBuildersoluzione e ho ottenuto 900 tic contro 875. Quindi è leggermente più lento.


@ Aaron.S - no non lo fa. Nel mio esempio "ABCDEFGHIJ"e "ZX"sono di diverse lunghezze.
John Alexiou,

3

Ancora un altro

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

2

Con l'aiuto di questo post, creo la seguente funzione con ulteriori controlli di lunghezza

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");
    }
}

2

Tutte le altre risposte non funzionano se la stringa contiene caratteri Unicode (come Emojis) perché un carattere Unicode pesa più byte di un carattere.

Esempio : l'emoji '🎶' convertito in byte, peserà l'equivalente di 2 caratteri. Pertanto, se il carattere unicode viene posizionato all'inizio della stringa, il offsetparametro verrà spostato).

Con questo argomento , estendo la classe StringInfo a Sostituisci per posizione mantenendo l'algoritmo di Nick Miller per evitare che:

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) : ""
        ));
    }
}

dotnetfiddle.net/l0dqS5 Non sono riuscito a far fallire altri metodi su Unicode. Modifica il violino per dimostrare il tuo caso d'uso. Molto interessato, ai risultati.
Markus

1
@MarkusHooge Prova a posizionare i caratteri unicode all'inizio della stringa come: dotnetfiddle.net/3V2K3Y . Vedrai solo l'ultima riga funziona bene e posiziona il carattere al 4 ° posto (nell'altro caso, il carattere unicode ha una lunghezza di 2 caratteri)
GGO

Hai ragione, non è chiaro. Ho aggiornato la mia risposta per aggiungere ulteriori spiegazioni sul perché le risposte degli altri non funzionano
GGO

1

Stavo cercando una soluzione con i seguenti requisiti:

  1. usa solo una singola espressione di una riga
  2. utilizzare solo metodi integrati nel sistema (nessuna utilità implementata personalizzata)

Soluzione 1

La soluzione più adatta a me è questa:

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

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

Soluzione 2

L'altra soluzione che soddisfa i miei requisiti è l'utilizzo Substringcon la concatenazione:

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

Anche questo va bene. Io immagino che non è efficiente come la prima esecuzione un saggio (a causa di inutili concatenazione di stringhe). Ma l'ottimizzazione prematura è la radice di tutti i mali .

Quindi scegli quello che ti piace di più :)


La soluzione 2 funziona ma richiede correzione: stringa newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
Yura G,

0

È meglio usare il String.substr().

Come questo:

ReplString = GivenStr.substr(0, PostostarRelStr)
           + GivenStr(PostostarRelStr, ReplString.lenght());

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

Lasciami spiegare la mia soluzione.
Data l'istruzione del problema di alterare una stringa nelle sue due posizioni specifiche ("posizione 4 in posizione 5") con due caratteri "Z" e "X" e si chiede di utilizzare l'indice di posizione per modificare la stringa e non la stringa Sostituisci ( ) metodo (potrebbe essere a causa della possibilità di ripetizione di alcuni caratteri nella stringa effettiva), preferirei utilizzare un approccio minimalista per raggiungere l'obiettivo rispetto all'uso Substring()e la stringa Concat()o la stringa Remove()e l' Insert()approccio. Sebbene tutte queste soluzioni serviranno allo scopo di raggiungere lo stesso obiettivo, ma dipende solo dalla scelta personale e dalla filosofia di assestamento con un approccio minimalista.
Tornando alla mia soluzione menzionata sopra, se diamo un'occhiata più da vicino a stringeStringBuilder, entrambi trattano internamente una determinata stringa come una matrice di caratteri. Se guardiamo all'implementazione di StringBuilderesso mantiene una variabile interna qualcosa come " internal char[] m_ChunkChars;" per catturare la stringa data. Ora, poiché questa è una variabile interna, non possiamo accedere direttamente alla stessa. Per il mondo esterno, essere in grado di accedere e modificare quella matrice di caratteri, StringBuilderli espone attraverso la proprietà dell'indicizzatore che assomiglia a qualcosa di seguito

    [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
        }
      }
    }

La mia soluzione sopra menzionata sfrutta questa capacità di indicizzatore per alterare direttamente l'array di caratteri gestito internamente che IMO è efficiente e minimalista.

BTW; possiamo riscrivere la soluzione sopra in modo più elaborato qualcosa di simile al di sotto

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

In questo contesto, vorrei anche ricordare che nel caso in cui stringabbia anche la proprietà indexer come mezzo per esporre la sua matrice di caratteri interna, ma in questo caso ha solo la proprietà Getter (e nessun Setter) in quanto la stringa è immutabile in natura. Ed è per questo che dobbiamo usare StringBuilderper modificare l'array di caratteri.

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

E, ultimo ma non meno importante, questa soluzione è adatta solo per questo specifico problema in cui la domanda è quella di sostituire solo pochi caratteri con un indice di posizione noto in anticipo. Potrebbe non essere la soluzione migliore quando il requisito è quello di modificare una stringa abbastanza lunga, ovvero il numero di caratteri da modificare sono di grandi dimensioni.


1
Modifica la tua risposta per spiegare come questo codice risponde alla domanda
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);

0

Ecco un semplice metodo di estensione:

    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));
    }

Usalo in questo modo:

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

-2

Credo che il modo più semplice sarebbe questo: (senza 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]);  
}

Questo funziona perché una variabile di tipo stringa può essere trattata come una matrice di variabili char. Ad esempio, puoi fare riferimento al secondo carattere di una variabile stringa con nome "myString" come myString [1]


Questo funziona solo perché nell'esempio dell'autore stringi personaggi da sostituire si presentano ciascuno una sola volta. Se corri contro, diciamo, "DBCDEFGHIE"allora ottieni "ZBCZXFGHIX", che non è il risultato desiderato. Inoltre, non sarei d'accordo sul fatto che sia più semplice delle altre non StringBuildersoluzioni pubblicate due anni e mezzo fa.
BACON,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.