138

String does not have ReplaceAt(), and I'm tumbling a bit on how to make a decent function that does what I need. I suppose the CPU cost is high, but the string sizes are small so it's all ok

joce
  • 9,624
  • 19
  • 56
  • 74
Jason94
  • 13,320
  • 37
  • 106
  • 184

7 Answers7

235

Use a StringBuilder:

StringBuilder sb = new StringBuilder(theString);
sb[index] = newChar;
theString = sb.ToString();
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 5
    @Jason94, I'm not sure this is more efficient than using ToCharArray as in Jon's answer, you should run tests to see which one is faster. – Thomas Levesque Feb 20 '12 at 20:18
  • 19
    I tried with a little benchmark on 100k iterations, ToCharArray is at least 2 time faster. – Matteo Migliore May 13 '13 at 08:55
  • 1
    `new StringBuilder(theString) {[index] = newChar}.ToString();` With object initializers you can make it a one-liner – Teun Nov 15 '18 at 09:54
96

The simplest approach would be something like:

public static string ReplaceAt(this string input, int index, char newChar)
{
    if (input == null)
    {
        throw new ArgumentNullException("input");
    }
    char[] chars = input.ToCharArray();
    chars[index] = newChar;
    return new string(chars);
}

This is now an extension method so you can use:

var foo = "hello".ReplaceAt(2, 'x');
Console.WriteLine(foo); // hexlo

It would be nice to think of some way that only required a single copy of the data to be made rather than the two here, but I'm not sure of any way of doing that. It's possible that this would do it:

public static string ReplaceAt(this string input, int index, char newChar)
{
    if (input == null)
    {
        throw new ArgumentNullException("input");
    }
    StringBuilder builder = new StringBuilder(input);
    builder[index] = newChar;
    return builder.ToString();
}

... I suspect it entirely depends on which version of the framework you're using.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 6
    +1 for making it an extension method. As mentioned in the comment of the accepted answer and also in other answers, the ToCharArray() method is faster than the StringBuilder method. One more possible improvement: check if input.Count > index-1 && index >= 0 otherwise you'll get an exception when you do chars[index] or builder[index]. – sth_Weird Jul 07 '16 at 09:08
40
string s = "ihj";
char[] array = s.ToCharArray();
array[1] = 'p';
s = new string(array);
Maciej
  • 7,871
  • 1
  • 31
  • 36
6

Strings are immutable objects, so you can't replace a given character in the string. What you can do is you can create a new string with the given character replaced.

But if you are to create a new string, why not use a StringBuilder:

string s = "abc";
StringBuilder sb = new StringBuilder(s);
sb[1] = 'x';
string newS = sb.ToString();

//newS = "axc";
Petar Ivanov
  • 91,536
  • 11
  • 82
  • 95
4

I suddenly needed to do this task and found this topic. So, this is my linq-style variant:

public static class Extensions
{
    public static string ReplaceAt(this string value, int index, char newchar)
    {
        if (value.Length <= index)
            return value;
        else
            return string.Concat(value.Select((c, i) => i == index ? newchar : c));
    }
}

and then, for example:

string instr = "Replace$dollar";
string outstr = instr.ReplaceAt(7, ' ');

In the end I needed to utilize .Net Framework 2, so I use a StringBuilder class variant though.

horgh
  • 17,918
  • 22
  • 68
  • 123
  • Wouldn't this be slow, for a long string? Why do this, rather than `StringBuilder` or `ToCharArray` solutions? It *might* be one less copy of the string (compared to `StringBuilder`), but I suspect a lot happens internally. – ToolmakerSteve Apr 08 '17 at 20:47
-2

If your project (.csproj) allow unsafe code probably this is the faster solution:

namespace System
{
  public static class StringExt
  {
    public static unsafe void ReplaceAt(this string source, int index, char value)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        if (index < 0 || index >= source.Length)
            throw new IndexOutOfRangeException("invalid index value");

        fixed (char* ptr = source)
        {
            ptr[index] = value;
        }
    }
  }
}

You may use it as extension method of String objects.

denys-vega
  • 3,522
  • 1
  • 19
  • 24
  • 4
    Of course, this violates the semantics of `string` - affects any other references to this object, nullifying/contradicting documentation that `string` objects are *immutable*. Doing so, to a built-in class, is highly dubious (strong code smell). Needs a giant WARNING comment slapped on it. Personally, I would **not** do this. If someone needs a *mutable* string, then create an appropriate class, subclassing or wrapping a `char[]`. – ToolmakerSteve Apr 08 '17 at 20:35
-4
public string ReplaceChar(string sourceString, char newChar, int charIndex)
    {
        try
        {
            // if the sourceString exists
            if (!String.IsNullOrEmpty(sourceString))
            {
                // verify the lenght is in range
                if (charIndex < sourceString.Length)
                {
                    // Get the oldChar
                    char oldChar = sourceString[charIndex];

                    // Replace out the char  ***WARNING - THIS CODE IS WRONG - it replaces ALL occurrences of oldChar in string!!!***
                    sourceString.Replace(oldChar, newChar);
                }
            }
        }
        catch (Exception error)
        {
            // for debugging only
            string err = error.ToString();
        }

        // return value
        return sourceString;
    }
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
user1054326
  • 318
  • 1
  • 3