HomeSearch

C# StringBuilder Examples

Improve string append performance with StringBuilder. Save allocations when building strings.
StringBuilder. Like the pyramids, a string can be built one piece at a time. For strings, each append causes a string copy. With StringBuilder we eliminate this copy.
Unlike a string, a StringBuilder can be changed. With it, an algorithm that modifies characters in a loop runs fast. Many string copies are avoided.
An intro. This program uses StringBuilder to build up a buffer of characters. We call Append() to add more data to our StringBuilder.

Append: This method can be called directly on its own result, in the same statement.

Because: Append(), and other methods like AppendFormat, return the same StringBuilder.

C# program that uses StringBuilder using System; using System.Text; class Program { static void Main() { StringBuilder builder = new StringBuilder(); // Append to StringBuilder. for (int i = 0; i < 10; i++) { builder.Append(i).Append(" "); } Console.WriteLine(builder); } } Output 0 1 2 3 4 5 6 7 8 9
Methods. Next, we look at other essential methods on StringBuilder. The methods shown here allow us to use it effectively in many programs. We append strings and lines.

Note: This example shows no loop, and is not ideal as a program. It is for demonstration purposes.

Append: Adds the contents of its argument to the buffer in the StringBuilder. Arguments are converted to strings with ToString.

Append

AppendLine: This does the same thing as Append, except with a newline on the end. We use a terse syntax form.

ToString: This returns the buffer. We will almost always want ToString—it will return the contents as a string.

C# program that uses StringBuilder methods using System; using System.Text; using System.Diagnostics; class Program { static void Main() { // Declare a new StringBuilder. StringBuilder builder = new StringBuilder(); builder.Append("The list starts here:"); builder.AppendLine(); builder.Append("1 cat").AppendLine(); // Get a reference to the StringBuilder's buffer content. string innerString = builder.ToString(); // Display with Debug. Debug.WriteLine(innerString); } } Output The list starts here: 1 cat
Replace. This method replaces all instances of one string with another. A string is required, but we do not need to use a string literal. The example exchanges "an" with "the."

Caution: The Replace method will replace all instances of the specified value. To replace one instance, we will need a custom method.

C# program that uses Replace using System; using System.Text; class Program { static void Main() { StringBuilder builder = new StringBuilder( "This is an example string that is an example."); builder.Replace("an", "the"); // Replaces 'an' with 'the'. Console.WriteLine(builder.ToString()); Console.ReadLine(); } } Output This is the example string that is the example.
AppendFormat. With this method, we add text to a StringBuilder based on a pattern. We can use substitution markers to fill fields in this pattern.AppendFormat

Tip: Many versions of AppendFormat in the .NET Framework (such as Console.WriteLine) are implemented with StringBuilder.

Console.WriteLine

However: It is usually faster to call Append repeatedly with all the required parts. But the syntax of AppendFormat may be clearer.

string.Format
C# program that uses AppendFormat using System; using System.Text; class Program { static void Main() { var builder = new StringBuilder(); // Append a format string directly. builder.AppendFormat("Hello {0}. It is {1}.", "Ankit", "Thursday"); Console.WriteLine(builder); } } Output Hello Ankit. It is Thursday.
Loops. Often we use StringBuilders in loops. If many appends are needed, sometimes StringBuilder is helpful in other contexts. Here is an example of StringBuilder in a foreach-loop.Foreach

Note: Many looping constructs, including for, while and foreach, are effective when used with StringBuilder.

C# program that uses foreach using System; using System.Text; class Program { static void Main() { string[] items = { "Cat", "Dog", "Celebrity" }; StringBuilder builder2 = new StringBuilder( "These items are required:").AppendLine(); foreach (string item in items) { builder2.Append(item).AppendLine(); } Console.WriteLine(builder2.ToString()); Console.ReadLine(); } } Output These items are required: Cat Dog Celebrity
Argument. StringBuilder can be passed as an argument. This is a nice optimization. It avoids converting back and forth to strings.

Tip: Eliminating allocations of strings (and StringBuilders) is an effective way to improve program performance.

Caution: Usually it is best to use descriptive names, not "A1" or "b." Code should describe its intent.

C# program that creates many StringBuilders using System; using System.Text; class Program { static string[] _items = new string[] { "cat", "dog", "giraffe" }; /// <summary> /// Append to a new StringBuilder and return it as a string. /// </summary> static string A1() { StringBuilder b = new StringBuilder(); foreach (string item in _items) { b.AppendLine(item); } return b.ToString(); } static void Main() { // Called in loop. A1(); } } C# program that uses StringBuilder argument using System; using System.Text; class Program { static string[] _items = new string[] { "cat", "dog", "giraffe" }; /// <summary> /// Append to the StringBuilder param, void method. /// </summary> static void A2(StringBuilder b) { foreach (string item in _items) { b.AppendLine(item); } } static void Main() { // Called in loop. StringBuilder b = new StringBuilder(); A2(b); } } Output Version 1: 5039 ms Version 2: 3073 ms
Indexer. It is possible to use the indexer to access or change certain characters. This syntax is the same as the syntax for accessing characters in a string instance.

Next: The example tests and changes characters in a StringBuilder. It uses the indexer.

Indexer
C# program that uses indexer using System; using System.Text; class Program { static void Main() { StringBuilder builder = new StringBuilder(); builder.Append("cat"); // Write second letter. Console.WriteLine(builder[1]); // Change first letter. builder[0] = 'r'; Console.WriteLine(builder.ToString()); } } Output a rat
Remove. This method removes a range of characters by index from the internal buffer. As with other StringBuilder methods, this just rearranges the internal buffer.

Here: We remove characters starting at index 4. We remove three characters past that index.

C# program that uses Remove using System; using System.Text; class Program { static void Main() { StringBuilder builder = new StringBuilder("Dot Net Perls"); builder.Remove(4, 3); Console.WriteLine(builder); } } Output Dot Perls
Append substring. We can append a substring directly from another string. No Substring call is needed. We use the Append method to do this.

IndexOf: We try to find the index of the space in the string. The char after the space is the start of the substring we want.

IndexOf

Append: We pass the string, the start index, and then the computed length (which continues until the end of the string).

Tip: Appending a substring directly is faster than calling Substring() to create an intermediate string first.

C# program that append Substring directly using System; using System.Text; class Program { static void Main() { var builder = new StringBuilder(); string value = "bird frog"; // Get the index of the char after the space. int afterSpace = value.IndexOf(' ') + 1; // Append a substring, computing the length of the target range. builder.Append(value, afterSpace, value.Length - afterSpace); Console.WriteLine("APPEND SUBSTRING: {0}", builder); } } Output APPEND SUBSTRING: frog
ToString. This method is optimized. It will not copy data in certain situations. These optimizations are hard to duplicate in custom code.

No arguments: We can call ToString with no arguments. This converts the entire StringBuilder into a string.

Range: We can pass a start index, and a length, to the ToString method. The second argument is the count of chars, not the end index.

Info: The ToString method has some advanced optimizations to reduce copying. It should be used when a string is required.

ToString
C# program that uses ToString using System; using System.Text; class Program { static void Main() { var builder = new StringBuilder("abcdef"); // Use ToString with no arguments. string result = builder.ToString(); Console.WriteLine("TOSTRING: {0}", result); // Use a start and length. string resultRange = builder.ToString(3, 3); Console.WriteLine("TOSTRING RANGE: {0}", resultRange); } } Output TOSTRING: abcdef TOSTRING RANGE: def
Trim. StringBuilder has no Trim, TrimStart or TrimEnd methods. But we can implement similar methods. Here we add a TrimEnd method that removes a final character.

TrimEnd: This custom method tests the last character of a StringBuilder for a matching char. It then reduces Length by 1 to erase it.

Caution: There are issues here. Only one char will be removed—we could use a while-loop to remove multiple matching chars.

C# program that implements Trim on StringBuilder using System; using System.Text; class Program { static void TrimEnd(StringBuilder builder, char letter) { // ... If last char matches argument, reduce length by 1. if (builder[builder.Length - 1] == letter) { builder.Length -= 1; } } static void Main() { StringBuilder builder = new StringBuilder(); builder.Append("This has an end period."); Console.WriteLine(builder); TrimEnd(builder, '.'); Console.WriteLine(builder); } } Output This has an end period. This has an end period
Performance, constructor. Often we need to append a string to a StringBuilder right after allocating it. When we specify that string in the constructor, performance improves.

Tip: This optimization can be used with a capacity. Specify the initial string value along with the capacity.

Version 1: This code creates a StringBuilder with capacity of 10 chars, and then appends the string "Cat" to it.

Version 2: This code creates a 10-char StringBuilder with the value "Cat" in the constructor.

Result: It is faster to initialize a StringBuilder with a string if we need to append something immediately to the StringBuilder.

C# program that benchmarks StringBuilder constructor using System; using System.Diagnostics; using System.Text; class Program { const int _max = 1000000; static void Main() { // Version 1: create StringBuilder then Append a string. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { var builder = new StringBuilder(10); builder.Append("Cat"); if (builder.Length == 0) { return; } } s1.Stop(); // Version 2: create a StringBuilder with a string argument. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { var builder = new StringBuilder("Cat", 10); if (builder.Length == 0) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } } Output 23.73 ns new StringBuilder, Append 19.42 ns new StringBuilder(string)
Performance, string concat. Sometimes we make a StringBuilder mistake that reduces speed. We use the plus operator on strings within a StringBuilder. This is bad.

Version 1: This concats 2 strings and then passes that string to the StringBuilder Append() method.

Version 2: This version appends each string individually, avoiding a temporary string creation.

Result: It is much faster to call Append() individually and never to create any temporary strings with the plus operator.

C# program that improves StringBuilder performance using System; using System.Diagnostics; using System.Text; class Program { const int _max = 1000000; static void Main() { var builder1 = new StringBuilder(); var builder2 = new StringBuilder(); var tempString = 100.ToString(); // Version 1: concat strings then Append. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { builder1.Append(tempString + ","); } s1.Stop(); // Version 2: append individual strings. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { builder2.Append(tempString).Append(","); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } } Output 38.92 ns Concat strings, then Append 15.42 ns Append twice
Performance, append substring. With the appropriate StringBuilder Append overload, we append a part of another string. This eliminates extra string copies.

Method 1: Here we take a substring of the input string and then append that to the StringBuilder.

Method 2: We call Append with 3 arguments. This is equivalent to the Substring call but much faster.

Result: The StringBuilder Append version that avoids a separate Substring call is faster.

SubstringOverload
C# program that uses StringBuilder overload using System; using System.Diagnostics; using System.Text; class Program { static void Method1(string input, StringBuilder buffer) { buffer.Clear(); string temp = input.Substring(3, 2); buffer.Append(temp); } static void Method2(string input, StringBuilder buffer) { buffer.Clear(); buffer.Append(input, 3, 2); } static void Main() { const int m = 100000000; var builder = new StringBuilder(); var s1 = Stopwatch.StartNew(); for (int i = 0; i < m; i++) { Method1("perls", builder); } s1.Stop(); var s2 = Stopwatch.StartNew(); for (int i = 0; i < m; i++) { Method2("perls", builder); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / m).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / m).ToString("0.00 ns")); Console.Read(); } } Output 33.47 ns Append string 25.14 ns Append range of string
Equals. This method compares the contents of 2 StringBuilders. It avoids lots of error-prone code that might otherwise be needed. It returns true or false.Equals

Caution: The Equals method will return false if the capacities of the objects are different, even if their data is identical.

Clear. To clear a StringBuilder, it is sometimes best to allocate a new one. Other times, we can assign the Length property to zero or use the Clear method.Clear
Exceptions. We get an ArgumentOutOfRangeException if we put too much data in a StringBuilder. The maximum number of characters is equal to Int32.MaxValue.ArgumentOutOfRangeException

Note: The Int32.MaxValue constant is equal to 2,147,483,647. This is the max length of a StringBuilder.

int.MaxValue
Immutable. This term indicates that an object's data is not changeable. To see an example of an immutable object, try to assign to a character in a string. This causes a compile-time error.

And: This happens because the string type does not define a set accessor. It cannot be modified once changed.

Arrays: Character arrays can be changed. Internally StringBuilder uses mutable char arrays for its buffer.

Memory. In garbage collection, there is memory pressure. As more temporary objects are created, GC runs more often. StringBuilder creates fewer temporary objects than string appends.StringBuilder Memory
Performance, char arrays. To use char arrays, code must be more precise. We must know a maximum or absolute size of the output string. This can enhance performance.Char Array
A review. StringBuilder is mainly a performance optimization. Here we test the performance and memory usage of it. In reviewing, we learn when it is superior to strings.StringBuilder: optimization
Some comments. These secrets are widely known. But this makes them no less effective. StringBuilder can improve the performance of a program—even if we misuse it.
© 2007-2019 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.
Home
Dot Net Perls