Comment remplacer une partie de chaîne par position?

155

J'ai cette chaîne: ABCDEFGHIJ

J'ai besoin de remplacer de la position 4 à la position 5 avec la chaîne ZX

Il ressemblera à ceci: ABCZXFGHIJ

Mais ne pas utiliser avec string.replace("DE","ZX")- je dois utiliser avec position

Comment puis-je le faire?

Gali
la source
@TotZam - veuillez vérifier les dates. Celui- ci est plus ancien que celui que vous avez lié.
ToolmakerSteve
@ToolmakerSteve Je regarde généralement la qualité de la question et des réponses, pas les dates, comme indiqué ici . Dans ce cas, il me semble avoir commis une erreur et cliqué sur la mauvaise pour marquer comme duplicata, car la qualité de cette question est évidemment meilleure, et j'ai donc signalé l'autre question.
Tot Zam
@TotZam - ah, je n'étais pas au courant de cette recommandation - merci de l'avoir signalé. (Bien qu'il soit déroutant de voir quelque chose de plus ancien signalé comme un double de quelque chose de plus récent, donc dans un tel cas, il serait utile d'expliquer explicitement que vous marquez comme duplication une question PLUS ANCIENNE, car la question liée a de meilleures réponses.)
ToolmakerSteve

Réponses:

198

Le moyen le plus simple d'ajouter et de supprimer des plages dans une chaîne consiste à utiliser le StringBuilder.

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

Une alternative est d'utiliser String.Substring, mais je pense que le StringBuildercode devient plus lisible.

Albin Sunnanbo
la source
6
serait mieux d'envelopper cela dans une méthode d'extension :)
balexandre
Pourquoi utilisez-vous un StringBuilder ici? Vérifiez la réponse de V4Vendetta, qui est beaucoup plus lisible ...
Fortega
1
@Fortega Remove () et Insert () créent et renvoient chacun une nouvelle chaîne. L'approche StringBuilder évite ce coût supplémentaire en travaillant dans une section pré-allouée de mémoire et en déplaçant les caractères autour d'elle.
redcalx
@redcalx C'est vrai, mais un coût supplémentaire est introduit en introduisant un objet StringBuilder et en réduisant la lisibilité
Fortega
2
Quel est l'intérêt d'utiliser StringBuilder ici, si vous appelez Remove and Insert de la même manière que V4Vendetta, mais qu'il le fait directement sur la chaîne? Semble un code redondant.
metabuddy
186
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
V4Vendetta
la source
17
Je préfère cela à l'initialisation d'un nouveau StringBuilder (...). Merci!
Michael Norgren
3
Je ne sais pas pourquoi quelqu'un utiliserait stringbuilder pour cette opération. C'est une bonne réponse
NappingRabbit
43

ReplaceAt (index int, longueur int, remplacement de chaîne)

Voici une méthode d'extension qui n'utilise ni StringBuilder ni 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 voulez que la chaîne de remplacement entière remplace autant de caractères que possible, définissez la longueur sur la longueur de la chaîne de remplacement:

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

Sinon, vous pouvez spécifier le nombre de caractères qui seront supprimés:

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

Si vous spécifiez la longueur à 0, alors cette fonction agit exactement comme la fonction d'insertion:

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

Je suppose que c'est plus efficace car la classe StringBuilder n'a pas besoin d'être initialisée et qu'elle utilise des opérations plus basiques. S'il vous plait corrigez moi si je me trompe. :)

Nicholas Miller
la source
5
Stringbuilder est plus efficace si la chaîne est plus longue que, disons 200 caractères.
Tim Schmelter
Pas de stress. A travaillé directement. Merci
D_Edet
9

Utilisez String.Substring()(détails ici ) pour couper la partie gauche, puis votre pièce de remplacement, puis la partie droite. Jouez avec les index jusqu'à ce que vous ayez raison :)

Quelque chose comme:

string replacement=original.Substring(0,start)+
    rep+original.Substring(start+rep.Length);
Daniel Mošmondor
la source
2
J'ai créé trois méthodes en utilisant les trois méthodes ci-dessus (string.Remove / Insert, Stringbuilder.Remove / Insert et Daniel's Substring answer, et SubString était la méthode la plus rapide.
Francine DeGrood Taylor
7

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();
    }
}
Ali Khalid
la source
5
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);
Javed Akram
la source
Cela fonctionne, vous ne voulez probablement pas remettre en tplace.
Pierre
4

Si vous vous souciez des performances, ce que vous voulez éviter ici, ce sont les allocations. Et si vous êtes sur .Net Core 2.1+ (ou le, encore inédit, .Net Standard 2.1), alors vous pouvez, en utilisant la string.Createméthode :

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.

svick
la source
3

Vous pouvez essayer quelque chose de lien ceci:

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

Comme d'autres l'ont mentionné, la Substring()fonction 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 également chronométré cela par rapport à la StringBuildersolution et j'ai obtenu 900 tics contre 875. C'est donc un peu plus lent.

John Alexiou
la source
@ Aaron.S - non, ce n'est pas le cas. Dans mon exemple "ABCDEFGHIJ"et "ZX"sont de longueurs différentes.
John Alexiou
3

Encore un autre

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

Avec l'aide de cet article, 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");
    }
}
Hassan
la source
2

Toutes les autres réponses ne fonctionnent pas si la chaîne contient un caractère Unicode (comme Emojis) car un caractère Unicode pèse plus d'octets qu'un caractère.

Exemple : l'emoji '🎶' converti en octets, pèsera l'équivalent de 2 caractères. Donc, si le caractère unicode est placé au début de votre chaîne, le offsetparamètre sera décalé).

Avec cette rubrique , j'étends la classe StringInfo à Replace by position en conservant l'algorithme de Nick Miller pour éviter cela:

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
la source
dotnetfiddle.net/l0dqS5 Je n'ai pas pu faire échouer d'autres méthodes sur Unicode. Veuillez modifier le violon pour illustrer votre cas d'utilisation. Très intéressé, par les résultats.
Markus
1
@MarkusHooge Essayez de placer les caractères Unicode au début de votre chaîne comme: dotnetfiddle.net/3V2K3Y . Vous verrez que seule la dernière ligne fonctionne bien et placez le caractère à la 4ème place (dans l'autre cas, le caractère unicode prend une longueur de 2 caractères)
GGO
Vous avez raison, ce n'est pas clair. J'ai mis à jour ma réponse pour ajouter plus d'explications sur les raisons pour lesquelles les autres réponses ne fonctionnent pas
GGO
1

Je cherchais une solution avec les exigences suivantes:

  1. n'utilisez qu'une seule expression sur une ligne
  2. utiliser uniquement des méthodes intégrées au système (pas d'utilitaire implémenté personnalisé)

Solution 1

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

Cela utilise StringBuilder.Replace(oldChar, newChar, position, count)

Solution 2

L'autre solution qui satisfait mes exigences est d'utiliser Substringavec la concaténation:

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

C'est bien aussi. Je suppose que 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 :)

KFL
la source
La solution 2 fonctionne mais nécessite une correction: string newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
Yura G du
0

Il est préférable d'utiliser le String.substr().

Comme ça:

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

Laissez-moi vous expliquer ma solution.
Étant donné le problème de la modification d'une chaîne dans ses deux positions spécifiques («position 4 à position 5») avec deux caractères 'Z' et 'X' et la demande est d'utiliser l'index de position pour modifier la chaîne et non la chaîne Remplacer ( ) (peut-être en raison de la possibilité de répétition de certains caractères dans la chaîne réelle), je préférerais utiliser une approche minimaliste pour atteindre l'objectif par rapport à l'utilisation d'une Substring()chaîne Concat()ou d'une chaîne Remove()et d'une Insert()approche. Bien que toutes ces solutions serviront l'objectif pour atteindre le même objectif, mais cela dépend juste du choix personnel et de la philosophie de régler avec une approche minimaliste.
Pour en revenir à ma solution mentionnée ci-dessus, si nous examinons de plus près stringetStringBuilder, tous deux traitent en interne une chaîne donnée comme un tableau de caractères. Si nous regardons son implémentation, StringBuilderil maintient une variable interne quelque chose comme « internal char[] m_ChunkChars;» pour capturer la chaîne donnée. Maintenant qu'il s'agit d'une variable interne, nous ne pouvons pas y accéder directement. Pour le monde externe, pour pouvoir accéder et modifier ce tableau de caractères, les StringBuilderexpose via la propriété d'indexeur 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é d'indexation pour modifier directement le tableau de caractères maintenu en interne, ce que l'OMI est efficace et minimaliste.

BTW; nous pouvons réécrire la solution ci-dessus de manière plus élaborée, comme ci-dessous

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

Dans ce contexte, je voudrais également mentionner que dans ce cas, stringil a également la propriété d'indexeur comme moyen d'exposer son tableau de caractères interne, mais dans ce cas, il 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 StringBuilderpour modifier le tableau de caractères.

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

Et last but not least, cette solution convient uniquement à ce problème spécifique où la demande est de ne remplacer que quelques caractères par un index de position connu à l'avance. Ce n'est peut-être pas la meilleure solution lorsque l'exigence est de modifier une chaîne assez longue, c'est-à-dire que le nombre de caractères à modifier est grand.

Comme si
la source
1
Veuillez modifier votre réponse pour expliquer comment ce code répond à la question
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);
user1355816
la source
0

Voici une méthode d'extension 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));
    }

Utilisez-le comme ceci:

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

Je pense que le moyen le plus simple serait le suivant: (sans 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]);  
}

Cela fonctionne car une variable de type string peut être traitée comme un tableau de variables char. Vous pouvez, par exemple, faire référence au deuxième caractère d'une variable de chaîne avec le nom "myString" comme myString [1]

Sfou
la source
Cela ne fonctionne que car dans l'exemple de l'auteur, stringles caractères à remplacer n'apparaissent qu'une seule fois. Si vous exécutez ceci contre, disons, "DBCDEFGHIE"alors vous obtenez "ZBCZXFGHIX", ce qui n'est pas le résultat souhaité. De plus, je ne suis pas d'accord pour dire que c'est plus simple que les autres non- StringBuildersolutions qui ont été affichées il y a deux ans et demi.
BACON