J'ai cette chaîne: ABCDEFGHIJ
Je dois remplacer de la position 4 à la position 5 par la chaîne ZX
Cela ressemblera à ceci: ABCZXFGHIJ
Mais ne pas utiliser avec string.replace("DE","ZX")
- Je dois utiliser avec position
Comment puis-je le faire?
La méthode la plus simple pour ajouter et supprimer des plages dans une chaîne consiste à utiliser StringBuilder
.
var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();
Une alternative consiste à utiliser String.Substring
, mais je pense que le code StringBuilder
devient plus lisible.
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
Voici une méthode d'extension qui n'utilise pas StringBuilder ou Substring. Cette méthode permet également à la chaîne de remplacement de s'étendre au-delà de la longueur de la chaîne source.
//// 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);
}
Lorsque vous utilisez cette fonction, si vous souhaitez que la chaîne de remplacement complète remplace autant de caractères que possible, définissez longueur par la longueur de la chaîne de remplacement:
"0123456789".ReplaceAt(7, 5, "Hello") = "0123456Hello"
Sinon, vous pouvez spécifier le nombre de caractères à supprimer:
"0123456789".ReplaceAt(2, 2, "Hello") = "01Hello456789"
Si vous spécifiez la longueur à 0, cette fonction agit comme la fonction d'insertion:
"0123456789".ReplaceAt(4, 0, "Hello") = "0123Hello456789"
Je suppose que cela est plus efficace puisque la classe StringBuilder n’a pas besoin d’être initialisée et qu’elle utilise davantage d’opérations de base. S'il vous plait corrigez moi si je me trompe. :)
Utilisez String.Substring()
(details here ) pour couper la partie gauche, puis votre pièce de rechange, puis la partie droite. Jouez avec les index jusqu'à ce que vous obteniez la bonne réponse :)
Quelque chose comme:
string replacement=original.Substring(0,start)+
rep+original.Substring(start+rep.Length);
En tant que méthode d'extension.
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);
Vous pouvez essayer quelque chose lier ceci:
string str = "ABCDEFGHIJ";
str = str.Substring(0, 2) + "ZX" + str.Substring(5);
Comme d'autres ont mentionné la fonction Substring()
est là pour une raison:
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);
}
J'ai aussi synchronisé ceci avec la solution StringBuilder
et obtenu 900 tics contre 875. Donc, c'est légèrement plus lent.
Toutes les autres réponses ne fonctionnent pas si la chaîne contient un caractère Unicode (comme Emojis).
Avec this topic , j'étend la classe StringInfo à Replace by position en conservant l'algorithme de Nick Miller
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) : ""
));
}
}
Avec l'aide de ce post, je crée la fonction suivante avec des contrôles de longueur supplémentaires
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");
}
}
Encore un autre
public static string ReplaceAtPosition(this string self, int position, string newValue)
{
return self.Remove(position, newValue.Length).Insert(position, newValue);
}
Je cherchais une solution avec les exigences suivantes:
La solution qui me convient le mieux est la suivante:
// replace `oldString[i]` with `c`
string newString = new StringBuilder(oldString).Replace(oldString[i], c, i, 1).ToString();
Ceci utilise StringBuilder.Replace(oldChar, newChar, position, count)
L’autre solution qui répond à mes besoins est d’utiliser Substring
avec concaténation:
string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);
C'est bien aussi. Je (suppose) ce n'est pas aussi efficace que le premier en termes de performances (en raison d'une concaténation de chaînes inutile). Mais l'optimisation prématurée est la racine de tout mal.
Alors, choisissez celui que vous aimez le plus :)
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();
Laissez-moi vous expliquer ma solution.
Compte tenu du problème posé par la modification d'une chaîne dans ses deux positions spécifiques ("position 4 à position 5") avec les caractères "Z" et "X" à deux caractères, la demande consiste à utiliser l'indice de position pour modifier la chaîne. et non pas la méthode de chaîne Replace () (peut-être à cause de la possibilité de répétition de certains caractères de la chaîne réelle), je préférerais utiliser une approche minimaliste pour atteindre l'objectif par rapport à l'utilisation de Substring()
et de la chaîne Concat()
ou de la chaîne Remove()
et de l'approche Insert()
. Toutes ces solutions serviront à atteindre le même objectif, mais cela dépend simplement du choix personnel et de la philosophie consistant à adopter une approche minimaliste.
Pour revenir à ma solution mentionnée ci-dessus, si nous examinons de plus près string
et StringBuilder
, tous les deux traitent en interne une chaîne donnée comme un tableau de caractères. Si nous examinons l'implémentation de StringBuilder
, il conserve une variable interne du type "internal char[] m_ChunkChars;
" pour capturer la chaîne donnée. Comme il s'agit d'une variable interne, nous ne pouvons pas accéder directement à la même chose. Pour que le monde externe puisse accéder et modifier ce tableau de caractères, StringBuilder
les expose à travers la propriété indexer qui ressemble à quelque chose comme ci-dessous
[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
}
}
}
Ma solution, mentionnée ci-dessus, tire parti de cette capacité de l'indexeur pour modifier directement le tableau de caractères maintenu en interne, ce qui est efficace et minimaliste.
BTW; nous pouvons réécrire la solution ci-dessus plus en détail quelque chose comme ci-dessous
string myString = "ABCDEFGHIJ";
StringBuilder tempString = new StringBuilder(myString);
tempString[3] = 'Z';
tempString[4] = 'X';
string modifiedString = tempString.ToString();
Dans ce contexte, vous voudrez également mentionner que, dans le cas de string
, la propriété indexer est également utilisée pour exposer son tableau de caractères interne, mais dans ce cas, elle n’a que la propriété Getter (et pas de Setter) car la chaîne est de nature immuable. Et c’est pourquoi nous devons utiliser StringBuilder
pour modifier le tableau de caractères.
[IndexerName("Chars")]
public extern char this[int index] { [SecuritySafeCritical, __DynamicallyInvokable, MethodImpl(MethodImplOptions.InternalCall)] get; }
Enfin et surtout, cette solution convient parfaitement à ce problème spécifique, dans lequel il est demandé de ne remplacer que quelques caractères avec un index de position connu. Ce n’est peut-être pas la meilleure solution lorsque l’obligation est de modifier une chaîne assez longue, c'est-à-dire que le nombre de caractères à modifier est élevé.
Si vous vous souciez de la performance, vous devez éviter les allocations. Et si vous êtes sur .Net Core 2.1+ (ou sur le .Net Standard 2.1, non encore publié), vous pouvez, en utilisant la méthode string.Create
:
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));
});
}
Cette approche est plus difficile à comprendre que les alternatives, mais c’est la seule qui n’allouera qu’un seul objet par appel: la chaîne nouvellement créée.
Il vaut mieux utiliser le String.substr()
.
Comme ça:
ReplString = GivenStr.substr(0, PostostarRelStr)
+ GivenStr(PostostarRelStr, ReplString.lenght());