Home
Search
StringBuilder ExamplesImprove string append performance with StringBuilder. Save allocations when building strings.
C#
This page was last reviewed on Nov 4, 2022.
StringBuilder. A C# string can be built one piece at a time, but 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.
First example. This program uses StringBuilder to build up a buffer of characters. We call Append() to add more data to our StringBuilder.
Part 1 We create a StringBuilder class instance (an object) by using the new-operator.
Part 2 We call Append(). This method can be called directly on its own result, in the same statement.
Info Append(), and other methods like AppendFormat, return the same StringBuilder.
using System; using System.Text; class Program { static void Main() { // Part 1: create new StringBuilder and loop. StringBuilder builder = new StringBuilder(); for (int i = 0; i < 3; i++) { // Part 2: append to StringBuilder. builder.Append(i).Append(" "); } Console.WriteLine(builder); } }
0 1 2
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 It is usually faster to call Append repeatedly with all the required parts. But the syntax of AppendFormat may be clearer.
using System; using System.Text; class Program { static void Main() { var builder = new StringBuilder(); // Append a format string directly. builder.AppendFormat("R: {0} ({1}).", "ABC", 1); Console.WriteLine(builder); } }
R: ABC (1).
AppendLine, ToString. To continue, we use other essential methods on StringBuilder. Methods like Append(), and ToString(), are used in most C# programs that create StringBuilders.
Part 1 We create a StringBuilder. It begins its existence empty, with no buffered characters.
Part 2 We call Append and AppendLine. Arguments are converted to strings with ToString. AppendLine appends a newline.
StringBuilder Append
Part 3 We call ToString. This returns the buffer. We will usually want ToString—it will return the contents as a string.
using System; using System.Text; class Program { static void Main() { // Part 1: declare a new StringBuilder. StringBuilder builder = new StringBuilder(); // Part 2: call Append and AppendLine. builder.Append("The list starts here:"); builder.AppendLine(); builder.Append("1 cat").AppendLine(); // Part 3: call ToString and display. string innerString = builder.ToString(); Console.WriteLine(innerString); } }
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."
Warning The Replace method will replace all instances of the specified value. To replace one instance, we will need a custom method.
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); } }
This is the example string that is the example.
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.
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); } }
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.
Warning Usually it is best to use descriptive names, not "A1" or "b." Not all examples can be perfect.
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() { StringBuilder b = new StringBuilder(); A2(b); } }
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
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()); } }
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 3 characters past that index.
using System; using System.Text; class Program { static void Main() { StringBuilder builder = new StringBuilder("Dot Net Perls"); builder.Remove(4, 3); Console.WriteLine(builder); } }
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.
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); } }
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.
StringBuilder 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); } }
TOSTRING: abcdef TOSTRING RANGE: def
AppendJoin. In .NET Core in 2020, we can invoke AppendJoin on the StringBuilder. This eliminates a loop: we can append many elements (joined together) in a single statement.
Part A We create a StringBuilder and a string array. We call AppendJoin on the StringBuilder—this is like calling string.Join and Append.
Join
Part B We can use AppendJoin with an int array. The ints are appended with inner separators.
Int, uint
using System; using System.Text; class Program { static void Main() { // Part A: use AppendJoin with string array. var builder = new StringBuilder(); string[] elements = { "bird", "frog", "dog" }; builder.AppendJoin(",", elements); Console.WriteLine(builder); // Part B: use AppendJoin with int array. builder.Clear(); int[] values = { 10, 20, 30 }; builder.AppendJoin(".", values); Console.WriteLine(builder); } }
bird,frog,dog 10.20.30
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.
Warning There are issues here. Only one char will be removed—we could use a while-loop to remove multiple matching chars.
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); } }
This has an end period. This has an end period
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.
using System; using System.Text; class Program { static void Main() { var builder = new StringBuilder(); for (int i = 0; i < 10; i++) { builder.Append(i); } Console.WriteLine("Before Clear(): {0}", builder.Length); builder.Clear(); Console.WriteLine("After Clear(): {0}", builder.Length); } }
Before Clear(): 10 After Clear(): 0
Benchmark, 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.
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")); } }
38.92 ns Concat strings, then Append 15.42 ns Append twice
Benchmark, append substring. With the appropriate StringBuilder Append overload, we append a part of another string. This eliminates extra string copies.
Version 1 Here we take a substring of the input string and then append that to the StringBuilder.
Version 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.
Substring
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(); // Version 1: take Substring. for (int i = 0; i < m; i++) { Method1("perls", builder); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: append range with Append. 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")); } }
33.47 ns Append string 25.14 ns Append range of string
Benchmark, CopyTo. With the CopyTo method on StringBuilder we can copy the StringBuilder data to a char array. But is this faster than using other methods?
Version 1 We use CopyTo on the StringBuilder to copy a 100-char buffer to a char array.
Char Array
Version 2 We use a for-loop to copy the data. Each element is individually assigned.
Result Using CopyTo on StringBuilder is much faster and should be always preferred when many characters are being copied.
using System; using System.Diagnostics; using System.Text; class Program { const int _max = 10000000; static void Main() { char[] array = new char[100]; var builder = new StringBuilder(new string('a', 100)); var s1 = Stopwatch.StartNew(); // Version 1: use CopyTo. for (int i = 0; i < _max; i++) { builder.CopyTo(0, array, 0, builder.Length); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use for-loop. for (int i = 0; i < _max; i++) { for (int z = 0; z < builder.Length; z++) { array[z] = builder[z]; } } 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")); } }
17.46 ns CopyTo 278.59 ns Assign elements
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.
StringBuilder Equals
Exceptions. We get an ArgumentOutOfRangeException if we put too much data in a StringBuilder. The maximum number of characters is equal to Int32.MaxValue.
ArgumentException
Note The Int32.MaxValue constant is equal to 2,147,483,647. This is the max length of a StringBuilder.
int.MaxValue
A summary. StringBuilder is mainly a performance optimization—it can reduce copying, allocations and thus garbage collector pressure. It helps optimize many C# programs.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Nov 4, 2022 (edit).
Home
Changes
© 2007-2023 Sam Allen.