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?
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?
Risposte:
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 StringBuilder
codice diventi più leggibile.
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
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. :)
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);
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();
}
}
string s = "ABCDEFG";
string t = "st";
s = s.Remove(4, t.Length);
s = s.Insert(4, t);
t
nuovo.
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.Create
metodo :
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.
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 StringBuilder
soluzione e ho ottenuto 900 tic contro 875. Quindi è leggermente più lento.
"ABCDEFGHIJ"
e "ZX"
sono di diverse lunghezze.
Ancora un altro
public static string ReplaceAtPosition(this string self, int position, string newValue)
{
return self.Remove(position, newValue.Length).Insert(position, newValue);
}
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");
}
}
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
offset
parametro 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) : ""
));
}
}
Stavo cercando una soluzione con i seguenti requisiti:
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)
L'altra soluzione che soddisfa i miei requisiti è l'utilizzo Substring
con 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ù :)
È meglio usare il String.substr()
.
Come questo:
ReplString = GivenStr.substr(0, PostostarRelStr)
+ GivenStr(PostostarRelStr, ReplString.lenght());
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 string
eStringBuilder
, entrambi trattano internamente una determinata stringa come una matrice di caratteri. Se guardiamo all'implementazione di StringBuilder
esso 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, StringBuilder
li 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 string
abbia 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 StringBuilder
per 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.
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);
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();
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]
string
i 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 StringBuilder
soluzioni pubblicate due anni e mezzo fa.