210

I have this string: ABCDEFGHIJ

I need to replace from position 4 to position 5 with the string ZX

It will look like this: ABCZXFGHIJ

But not to use with string.replace("DE","ZX") - I need to use with position

How can I do it?

Mahmoud Farahat
  • 5,364
  • 4
  • 43
  • 59
Gali
  • 14,511
  • 28
  • 80
  • 105
  • @TotZam - please check the dates. *This* one is older than the one you linked. – ToolmakerSteve Apr 08 '17 at 20:55
  • @ToolmakerSteve I usually look at the quality of the question and answers, not the dates, as said to do [here](https://meta.stackexchange.com/a/147651/297762). In this case, I seem to have made a mistake and clicked the wrong one to mark as duplicate, since the quality of this question is obviously better, and so I have flagged the other question. – Tot Zam Apr 10 '17 at 13:37
  • @TotZam - ah, I did not know about that recommendation - thanks for pointing it out. (Though it is confusing to see something older reported as a duplicate of something newer, so in such a case, it would be worth explicitly explaining that you are marking as duplicate an OLDER question, because the linked one has better answers.) – ToolmakerSteve Apr 10 '17 at 18:35

21 Answers21

280
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
V4Vendetta
  • 37,194
  • 9
  • 78
  • 82
231

The easiest way to add and remove ranges in a string is to use the StringBuilder.

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

An alternative is to use String.Substring, but I think the StringBuilder code gets more readable.

James Ko
  • 32,215
  • 30
  • 128
  • 239
Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
  • 6
    would be better wrapping that up in an extension method :) – balexandre Feb 16 '11 at 11:08
  • 2
    Why do you use a StringBuilder here? Check V4Vendetta's answer, which is much more readable... – Fortega Apr 28 '17 at 14:19
  • 3
    @Fortega Remove() and Insert() each create and return a new string. The StringBuilder approach avoid this extra cost by working within a pre-allocated section of memory and moving chars round within it. – redcalx Apr 17 '18 at 15:52
  • @redcalx That's true, but an extra cost is introduced by introducing a StringBuilder object and by reducing readability – Fortega Apr 23 '18 at 14:21
  • 4
    What's the point of using StringBuilder here, if you're calling Remove and Insert the same way as V4Vendetta, but he is doing it directly on the string? Seems redundant code. – HamsterWithPitchfork Nov 02 '18 at 05:35
  • don't argue. if you do only one or two replacements, it probably does not matter and you can choose whatever variant you like best. However, if you do lots of replacements, you might want to use [Benchmark.NET](https://benchmarkdotnet.org/) to figure out which method works best for your use case. – MovGP0 May 28 '20 at 13:31
55

ReplaceAt(int index, int length, string replace)

Here's an extension method that doesn't use StringBuilder or Substring. This method also allows the replacement string to extend past the length of the source string.

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

When using this function, if you want the entire replacement string to replace as many characters as possible, then set length to the length of the replacement string:

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

Otherwise, you can specify the amount of characters that will be removed:

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

If you specify the length to be 0, then this function acts just like the insert function:

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

I guess this is more efficient since the StringBuilder class need not be initialized and since it uses more basic operations. Please correct me if I am wrong. :)

Community
  • 1
  • 1
Nicholas Miller
  • 4,205
  • 2
  • 39
  • 62
13

If you care about performance, then the thing you want to avoid here are allocations. And if you're on .Net Core 2.1+, then you can, by using the string.Create method:

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

This approach is harder to understand than the alternatives, but it's the only one that will allocate only one object per call: the newly created string.

svick
  • 236,525
  • 50
  • 385
  • 514
10

Use String.Substring() (details here) to cut left part, then your replacement, then right part. Play with indexes until you get it right :)

Something like:

string replacement=original.Substring(0,start)+
    rep+original.Substring(start+rep.Length);
Daniel Mošmondor
  • 19,718
  • 12
  • 58
  • 99
  • 3
    I created three methods using the above three methods (string.Remove/Insert, Stringbuilder.Remove/Insert and Daniel's Substring answer, and SubString was the fastest method. – Francine DeGrood Taylor Feb 26 '15 at 20:52
8

As an extension method.

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
  • 1,345
  • 8
  • 19
8
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);
Javed Akram
  • 15,024
  • 26
  • 81
  • 118
4

Like other have mentioned the Substring() function is there for a reason:

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

Also I timed this against the StringBuilder solution and got 900 tics vs. 875. So it is slightly slower.

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
4

Yet another

    public static string ReplaceAtPosition(this string self, int position, string newValue)        
    {
        return self.Remove(position, newValue.Length).Insert(position, newValue); 
    }
Stijn Van Antwerpen
  • 1,840
  • 17
  • 42
4

You could try something link this:

string str = "ABCDEFGHIJ";
str = str.Substring(0, 2) + "ZX" + str.Substring(5);
MBU
  • 4,998
  • 12
  • 59
  • 98
3

With the help of this post, I create following function with additional length checks

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
  • 742
  • 7
  • 13
3

All others answers don't work if the string contains Unicode char (like Emojis) because an Unicode char weight more bytes than a char.

Example : the emoji '' converted to bytes, will weight the equivalent of 2 chars. So, if the unicode char is placed at the beginning of your string, offset parameter will be shifted).

With this topic, i extend the StringInfo class to Replace by position keeping the Nick Miller's algorithm to avoid that :

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
  • 2,678
  • 4
  • 20
  • 42
  • https://dotnetfiddle.net/l0dqS5 I could not get other methods to fail on Unicode. Please alter the fiddle to demonstrate your use case. Very interested, in results. – Markus Mar 14 '19 at 15:12
  • 1
    @MarkusHooge Try to place the unicode chars at the beginning of your string like : https://dotnetfiddle.net/3V2K3Y. You'll see only the last line works well and place the char to the 4th place (in the other case, unicode char takes 2 char length) – GGO Mar 15 '19 at 09:08
  • You're right, it's not clear. I updated my answer to add more explanations why the others answer don't work – GGO Mar 15 '19 at 09:23
3
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();

Let me explain my solution.
Given the problem statement of altering a string in its two specific position (“position 4 to position 5”) with two character ‘Z’ and ‘X’ and the ask is to use the position index to alter the string and not string Replace() method(may be because of the possibility of repetition of some characters in the actual string), I would prefer to use minimalist approach to achieve the goal over using Substring() and string Concat() or string Remove() and Insert() approach. Though all those solutions will serve the purpose in attaining the same goal, but it just depends on personal choice and philosophy of settling with minimalist approach.
Coming back to my solution mention above, if we take a closer look of string and StringBuilder, both of them internally treats a given string as an array of characters. If we look at the implementation of StringBuilder it maintains an internal variable something like “internal char[] m_ChunkChars;” to capture the given string. Now since this is an internal variable, we cannot directly access the same. For external world, to be able to access and alter that character array, StringBuilder exposes them through indexer property which looks something like below

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

My solution mentioned above leverage this indexer capability to directly alter the internally maintained character array which IMO is efficient and minimalist.

BTW; we can rewrite the above solution more elaborately something like below

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

In this context also would like to mention that in case of string it also have indexer property as a means to expose its internal character array, but in this case it only has Getter property (and no Setter) as string is immutable in nature. And that is why we need to use StringBuilder to alter the character array.

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

And last but not the least this solution is only best fit for this specific problem where the ask is to replace only few characters with a known position index upfront. It may not be the best fit when the requirement is to alter a fairly lengthy string i.e. number of characters to alter are large in numbers.

Asif
  • 329
  • 1
  • 7
2

I was looking for a solution with following requirements:

  1. use only a single, one-line expression
  2. use only system builtin methods (no custom implemented utility)

Solution 1

The solution that best suits me is this:

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

This uses StringBuilder.Replace(oldChar, newChar, position, count)

Solution 2

The other solution that satisfies my requirements is to use Substring with concatenation:

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

This is OK too. I guess it's not as efficient as the first one performance wise (due to unnecessary string concatenation). But premature optimization is the root of all evil.

So pick the one that you like the most :)

KFL
  • 17,162
  • 17
  • 65
  • 89
  • Solution 2 works but requires correction: string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length-(i+1)); – Yura G Sep 11 '19 at 13:49
  • Better yet, just `string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1)`. By default without a length, .Substring will just pull the rest of the string. – Sloan Reynolds Jul 05 '21 at 03:30
1

Suppose we know the index of string to be replace.

    string s = "ABCDEFGDEJ";
    string z = "DE";
    int i = s.IndexOf(z);
    if(i == 3)
        s = s.Remove(3,z.Length).Insert(3,"ZX");
    //s = ABCZXFGDEJ
ZooboMe23
  • 23
  • 2
1

hello this code helps me:

var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();
Sylla
  • 87
  • 8
0

It's better to use the String.substr().

Like this:

ReplString = GivenStr.substr(0, PostostarRelStr)
           + GivenStr(PostostarRelStr, ReplString.lenght());
Shaikh Farooque
  • 2,620
  • 1
  • 19
  • 33
Varun Kumar
  • 186
  • 1
  • 9
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);
Dharman
  • 30,962
  • 25
  • 85
  • 135
0

Here is a simple extension method:

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

Use it like this:

var theString = new string(' ', 10);
var sb = new StringBuilder(theString);
sb.Replace(5, "foo");
return sb.ToString();
MovGP0
  • 7,267
  • 3
  • 49
  • 42
0

I do this

Dim QTT As Double
                If IsDBNull(dr.Item(7)) Then
                    QTT = 0
                Else
                    Dim value As String = dr.Item(7).ToString()
                    Dim posicpoint As Integer = value.LastIndexOf(".")
                    If posicpoint > 0 Then
                        Dim v As New Text.StringBuilder(value)
                        v.Remove(posicpoint, 1)
                        v.Insert(posicpoint, ",")
                        QTT = Convert.ToDouble(v.ToString())
                    Else
                        QTT = Convert.ToDouble(dr.Item(7).ToString())
                    End If
                    Console.WriteLine(QTT.ToString())
                End If
-2

I believe the simplest way would be this:(without 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]);  
}

This works because a variable of type string can be treated as an array of char variables. You can, for example refer to the second character of a string variable with name "myString" as myString[1]

Sfou
  • 52
  • 8
  • 1
    This only works because in the author's example `string` the characters to be replaced each occur only once. If you run this against, say, `"DBCDEFGHIE"` then you get `"ZBCZXFGHIX"`, which is not the desired result. Also, I would not agree that this is simpler than the other non-`StringBuilder` solutions that were posted two and a half years ago. – Lance U. Matthews Aug 31 '13 at 16:27