14

What's the cleanest way of editing the characters in a string in C#?

What's the C# equivalent of this in C++:

std::string myString = "boom";
myString[0] = "d";
3
  • 3
    Given the immutability issue, I think the best answer would depend on what you know about your strings and which characters are likely to change and with what frequency. Commented May 28, 2010 at 17:56
  • @Chris Farmer: Actually I doubt it would change the answer much. Any reasonably performant implementation will either use a char array or StringBuilder. Which one is "best" is primarily a matter of taste. Commented May 28, 2010 at 19:16
  • @hemp: I don't disagree. I had written my comment before I saw the StringBuilder suggestion. I think it's probably a great way to handle almost any of these cases. Commented May 28, 2010 at 19:59

11 Answers 11

32

Use a StringBuilder instead.

string is immutable as described by MSDN:

Strings are immutable--the contents of a string object cannot be changed after the object is created, although the syntax makes it appear as if you can do this.

So you want something like:

 StringBuilder sb = new StringBuilder("Bello World!");
 sb[0] = 'H';
 string str = sb.ToString();
Sign up to request clarification or add additional context in comments.

6 Comments

Got my upvote because it's the only other answer that gives a flexible way to actually do what he wants, correctly, without expensive or over-elaborate constructs.
@tesserex - I don't deny that this is flexible, but I would hardly consider mine expensive or over-elaborate. In fact, it's identical to mine except it uses a -more- elaborate construct.
@glowcoder - your answer is identical to mine, so you are exempt from my previous comment. I was referring to the use of Replace or Substring.
@Tesserex: I posted one that doesn't use Replace, Substring, or StringBuilder. :)
@hemp - I think "over-elaborate constructs" adequately describes your answer :)
|
15

Decided to time what I felt where the two most canonical approaches, plus one I threw in as unrepresented; here's what I found (Release build):

ReplaceAtChars: 86ms
ReplaceAtSubstring: 258ms
ReplaceAtStringBuilder: 161ms

Clearly the Char array approach is by far best optimized by the runtime. Which actually suggests that the current leading answer (StringBuilder) is likely not the best answer.

And here was the test I used:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("ReplaceAtChars: " + new Stopwatch().Time(() => "test".ReplaceAtChars(1, 'E').ReplaceAtChars(3, 'T'), 1000000) + "ms");
        Console.WriteLine("ReplaceAtSubstring: " + new Stopwatch().Time(() => "test".ReplaceAtSubstring(1, 'E').ReplaceAtSubstring(3, 'T'), 1000000) + "ms");
        Console.WriteLine("ReplaceAtStringBuilder: " + new Stopwatch().Time(() => "test".ReplaceAtStringBuilder(1, 'E').ReplaceAtStringBuilder(3, 'T'), 1000000) + "ms");
    }
}

public static class ReplaceAtExtensions
{
    public static string ReplaceAtChars(this string source, int index, char replacement)
    {
        var temp = source.ToCharArray();
        temp[index] = replacement;
        return new String(temp);
    }

    public static string ReplaceAtStringBuilder(this string source, int index, char replacement)
    {
        var sb = new StringBuilder(source);
        sb[index] = replacement;
        return sb.ToString();
    }

    public static string ReplaceAtSubstring(this string source, int index, char replacement)
    {
        return source.Substring(0, index) + replacement + source.Substring(index + 1);
    }
}

public static class StopwatchExtensions
{
    public static long Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Reset();
        sw.Start();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.ElapsedMilliseconds;
    }
}

Comments

8

Strings in .Net are immutable. That means you can't edit them. All you can do is make new ones. You have to do something like this:

string myString = "boom";
myString = "d" + myString.Substring(1);

1 Comment

this works but would quickly get tedious if he has to edit multiple characters in longer strings.
8

Strings are immutable, so you can't just change it.

You could get a char[] from the string, make your changes, and make a new string from the altered array.

You could use things like replace, although they don't give you control over the per-char basis as C like. They also will make a new string with every change, where the char[] style allows you to make all changes and then make a new string from it.

Comments

5

You can't since strings are immutable.

You could always roll your own extension method to do something similar:

public static string ReplaceAtIndex(this string original, 
    int index, char character)
{
    char[] temp = original.ToCharArray();
    temp[index] = character;
    return new String(temp);
}

And call it like:

string newString = myString.ReplaceAtIndex(0, 'd');

4 Comments

Each call to that function would create 3 temporary strings. That's not terrible, but if you needed to do this a bunch of times it would get quite expensive.
@hemp - definitely never said it was the best implementation. heh. I'll update with something a little better.
+1 Nice answer - what is the overloading like construct you've used there called in C#?
@David Relihan - It's call an Extension Method. You can read more about them here: msdn.microsoft.com/en-us/library/bb383977.aspx
4

As Brian said, use a StringBuilder, instead:

string myString = "boom";
StringBuilder myStringBuilder = new StringBuilder(myString);
myStringBuilder[0] = 'd';
// Whatever else you're going to do to it

Whenever you need the string again, call myStringBuilder.ToString()

1 Comment

Yup - will probably go with this
3
string myString = "boom";
char[] arr = myString.ToCharArray();
arr[0] = 'd';
myString = new String(arr);

1 Comment

As my timing test shows, this algorithm actually performs better than StringBuilder.
3
string myString = "boom";
char[] myChars = myString.ToCharArray();
myChars[0] = 'd';
myString = new String(myChars);

2 Comments

you can't just call ToString() on a character array. It isn't overloaded to convert to a string. It just gives you "System.Char[]"
Yes, you are correct, that gets me every time. I have edited appropriately. Thanks.
3

Strings are immutable in C#. Use the StringBuilder class or a character array.

StringBuilder sb = new StringBuilder("boom");
sb[0] = 'd';

IIRC, .NET uses what is called String Pooling. Every time a new string literal is created, it is stored in memory as part of the string pool. If you create a second string that matches a string in the string pool, both variables will reference the same memory.

When you try to do an operation like you did to replace the 'b' character with a 'd' character using strings in .NET, your program is actually creating a second string in the string pool with the value "doom", although from your perspective it does not appear as if this is happening at all. By reading the code, one would assume that the character is being replaced.

I brought this up because I encounter this question all the time, and people often ask why they should be using the StringBuilder when a string can do the same thing. Well technically it can't, but it's designed in a way to appear as if it can.

1 Comment

Thanks for that info. - Are StringBuilder strings created in the string pool?
2

Here's a fun one I put together. Now, please bear in mind this is not very efficient, especially for simple replacements. However, it was fun to write and lends itself to a fairly readable usage pattern. It also highlights the little known fact that String implements IEnumerable.

public static class LinqToStrings
{
    public static IQueryable<char> LinqReplace(this string source, int startIndex, int length, string replacement)
    {
        var querySource = source.AsQueryable();
        return querySource.LinqReplace(startIndex, length, replacement);
    }

    public static IQueryable<char> LinqReplace(this IQueryable<char> source, int startIndex, int length, string replacement)
    {
        var querySource = source.AsQueryable();
        return querySource.Take(startIndex).Concat(replacement).Concat(querySource.Skip(startIndex + length));
    }

    public static string AsString(this IQueryable<char> source)
    {
        return new string(source.ToArray());
    }
}

And here's some example usage:

public void test()
{
    var test = "test";
    Console.WriteLine("Old: " + test);
    Console.WriteLine("New: " + test.LinqReplace(0, 4, "SOMEPIG")
                                    .LinqReplace(4, 0, "terrific")
                                    .AsString());
}

Outputs:

Old: test
New: SOMEterrificPIG

Another version of that same approach, which is not so horrifically slow is straightforward using Substring:

public static string ReplaceAt(this string source, int startIndex, int length, string replacement)
{
    return source.Substring(0, startIndex) + replacement + source.Substring(startIndex + length);
}

And in a wonderful example of why you should profile your code, and why you probably should not use my LinqToStrings implementation in production code, here's a timing test:

Console.WriteLine("Using LinqToStrings: " + new Stopwatch().Time(() => "test".LinqReplace(0, 4, "SOMEPIG").LinqReplace(4, 0, "terrific").AsString(), 1000));
Console.WriteLine("Using Substrings: " + new Stopwatch().Time(() => "test".ReplaceAt(0, 4, "SOMEPIG").ReplaceAt(4, 0, "terrific"), 1000));

Which measures timer ticks in 1,000 iterations, producing this output:

Using LinqToStrings: 3,818,953
Using Substrings: 1,157

2 Comments

It's cool... but it doesn't really address the problem. He wants to modify, not insert. Still, I like the approach, and you're right about both the pattern being good, and the performance being... not so good!
Actually, it does address the problem. Since it takes a start index and length it can be used to insert, remove or replace. My example actually shows replace and insert ("test" becomes "SOMEPIG" which then becomes "SOMEterrificPIG".) That said - it's not an approach I recommend unless you're bored!
1

Try using string.replace

http://msdn.microsoft.com/en-us/library/fk49wtc1.aspx

Probably your cleanest, easiest way.

1 Comment

Was looking at that but my ise case if for editing specific places in the string

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.