In the world of software development, we sometimes face requirements to shorten strings within our applications. A common use case is displaying text in user interfaces, such as labels, buttons, or columns in a table. To ensure that the text fits within a limited space, we may need to truncate it. This allows us to display a more concise version without overflowing or breaking the layout. This article explores different methods of string truncation in .NET and compares their performance.

To download the source code for this article, you can visit our GitHub repository.

To simplify the examples we will employ the Strategy Design Pattern.

Initial Setup

First, we need to have some initial setup. Consider the method signature:

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

TruncateString(string originalString, int maxLength)

Here originalString is the string to truncate and maxLength is the maximum allowed length before truncation. Since we cannot shorten the string to a negative length and we will not return a string greater than originalString.Length we need to perform some validation on the input parameters. Also, since we’ll be reviewing multiple truncation methods and almost all of them will require such a check, it will be helpful to move it to a separate method.

Here is where the strategy pattern helps to simplify our coding:

private static string TruncateString(string originalString, int maxLength,
    Func<string, int, string> specificTruncateMethod)
{
    if (maxLength <= 0)
    {
        return string.Empty;
    }

    if (maxLength >= originalString.Length)
    {
        return originalString;
    }

    return specificTruncateMethod(originalString, maxLength);
}

TruncateString() takes three parameters: the string to truncate, the maximum length of the resultant string, and a Func representing the specific truncation method to employ.

First, we check for a 0 or negative maximum length and return the empty string in that case. Next, we compare the maximum length with the original string length and if it is equal to or exceeds the original length, we simply return the original string. Lastly, having validated that the maximum length is within the bounds of the string, we truncate using the specified method.

Now let’s take a look at different ways that we can truncate a string.

Truncate a String Using Substring

Let’s create a truncation method using Substring():

public static string TruncateWithSubstring(string originalString, int maxLength)
{
    return TruncateString(originalString, maxLength, UsingSubstring);

    static string UsingSubstring(string str, int length) =>
        str.Substring(0, length);
}

Inside the TruncateWithSubstring() method, we define a local function UsingSubstring(), which utilizes Substring() to truncate a string. Substring() extracts a substring starting from the specified index (0 in our example) up to the maximum length and returns it.

It is important to note that Substring() throws an ArgumentOutOfRangeException if the starting index is negative or exceeds the original string’s length, or if the length parameter is negative or results in an index beyond the original string’s boundaries. In our case, because we are handling the bounds checks in TruncateString() , we don’t have to worry about an ArgumentOutOfRangeException from our use of Substring()

Starting with C# 8.0 it’s possible to use the range operator instead of the Substring():

static string UsingSubstring(string str, int length) => str[..length];

Under the hood, this is still calling Substring(), but our line of code is much shorter.

For subsequent methods, the only thing that will require change is our local function. So for brevity’s sake, we will just show the local functions.

Truncate a String Using Remove

Let’s see how we can use the Remove() method to truncate a string:

static string UsingRemove(string str, int length) => str.Remove(length);

UsingRemove(), just like the previous technique,  takes the original string and the maximum length as input parameters. It then calls Remove() which returns a new string, removing all characters from the maximum length position to the end of the string.

Truncate a String Using a For Loop

Now let’s see how we can achieve this using a for loop in conjunction with the StringBuilder class:

static string UsingForLoopStringBuilder(string str, int length)
{
    var truncatedStringBuilder = new StringBuilder(length);

    for (int i = 0; i < length; i++)
    {
        truncatedStringBuilder.Append(str[i]);
    }

    return truncatedStringBuilder.ToString();
}

The UsingForLoopStringBuilder() method utilizes the StringBuilder class and a for loop. It truncates a string by iteratively appending characters to a StringBuilder object, then converting the result back to a string and returning it.

To learn more about StringBuilder, please read our article on StringBuilder in C#.

Truncate a String Using LINQ

Let’s take a look at how we can do this using LINQ:

static string UsingLINQ(string str, int length) => new(str.Take(length).ToArray());

The UsingLINQ() method utilizes LINQ to truncate a string. First, it takes the first length characters from the str. It converts the characters into an array, and then constructs a new string from this array. The truncated string is returned as the result.

To learn more about LINQ, read our article on LINQ Basic Concepts in C#.

Truncate a String Using Regular Expressions (Regex)

Let’s take a look at another way we can truncate a string using Regex:

static string UsingRegularExpressions(string str, int length)
{
    return Regex.Replace(str, $"^(.{{0,{length}}}).*$", "$1");
}

The UsingRegularExpressions() method truncates the str using regular expressions. It uses the Regex.Replace() method to capture a substring of the str that starts from the beginning and can be at most length characters long. The regular expression “^(.{{0,{length}}}).*$” is used to capture this substring and the “$1” replacement pattern retains the captured portion, effectively truncating the string. The truncated string is then returned as the result.

To learn more about regular expressions, read our article on Introduction to Regular Expressions in C#.

Truncate a String Using ReadOnlySpan

Let’s take a look at one more way we can truncate a string using ReadOnlySpan<char>:

static string UsingSpan(string str, int length) => str.AsSpan()[..length].ToString();

First, we get a ReadOnlySpan<char> over the original string. Next, we use the range operator to create a slice of the specified length from the span, and then return a new string from the slice.

Performance comparison

Now let’s compare the performance of all the different methods we have discussed.

To get a good understanding of Benchmarking, feel free to read Introduction to Benchmarking in C# and ASP.NET Core Projects.

We’ll use a 1000-character string for our benchmarks and test truncating at different percentages of the string length: 1% (10 chars), 50% (500 chars), and 95% (950 chars):

| Method                           | MaxLength | Mean         | Error      | StdDev     |
|--------------------------------- |---------- |-------------:|-----------:|-----------:|
| TruncateWithSubstring            | 10        |     8.971 ns |  0.2044 ns |  0.3241 ns |
| TruncateWithRemove               | 10        |    12.592 ns |  0.3165 ns |  0.9081 ns |
| TruncateWithSpan                 | 10        |    15.065 ns |  0.3541 ns |  1.0273 ns |
| TruncateWithForLoopStringBuilder | 10        |    40.181 ns |  0.8878 ns |  2.6178 ns |
| TruncateWithLINQ                 | 10        |   119.187 ns |  2.4247 ns |  6.1717 ns |
| TruncateWithRegularExpressions   | 10        |   459.498 ns |  8.9963 ns | 14.2691 ns |
|                                  |           |              |            |            |
| TruncateWithRemove               | 500       |    51.243 ns |  1.0269 ns |  1.0988 ns |
| TruncateWithSubstring            | 500       |    51.802 ns |  1.0596 ns |  1.2203 ns |
| TruncateWithSpan                 | 500       |    53.799 ns |  0.8860 ns |  1.0881 ns |
| TruncateWithRegularExpressions   | 500       |   478.070 ns |  9.5975 ns | 16.2973 ns |
| TruncateWithForLoopStringBuilder | 500       |   939.570 ns | 12.3526 ns | 10.3150 ns |
| TruncateWithLINQ                 | 500       | 1,510.720 ns | 28.3765 ns | 26.5434 ns |
|                                  |           |              |            |            |
| TruncateWithRemove               | 950       |    92.746 ns |  1.8970 ns |  2.8393 ns |
| TruncateWithSubstring            | 950       |    94.179 ns |  1.7420 ns |  2.3845 ns |
| TruncateWithSpan                 | 950       |    97.031 ns |  1.9873 ns |  4.5662 ns |
| TruncateWithRegularExpressions   | 950       |   528.688 ns | 10.0907 ns |  9.4388 ns |
| TruncateWithForLoopStringBuilder | 950       | 1,448.205 ns | 28.8927 ns | 51.3567 ns |
| TruncateWithLINQ                 | 950       | 2,823.249 ns | 56.3988 ns | 95.7694 ns |

From the results of our benchmarking, the Substring()Remove() and ReadOnlySpan<char> methods have the best performance. These methods demonstrated the fastest mean execution times.

While the performance using regular expressions is very consistent, being almost identical for each of the cases, overall it still performs ~5x slower than the top three methods. Finally, rounding out the bottom are our for-loop and LINQ implementations, which are ~16x and ~30x slower respectively.

Conclusion

In this article, we have explored multiple ways of truncating strings in .NET. We have also analyzed how well each of them does in terms of performance. We saw how there is very little performance difference between the use of Substring(), Remove(), and ReadOnlySpan<char>. They also have the added advantage of being simple, readable, and very straightforward, which goes a long way to aiding code maintainability.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!