Walkthrough of the String Trim Function In C#

Walkthrough of the String Trim Function Banner Image

What is String Trim?

In C#, string trim removes space, tabs, and various lengths of whitespace at the beginning or end of a string. This is good for user inputs, or forms where users may have accidentally put a space or copied some text from somewhere else that had a space in it. The other function overloads of trim allow us to find and remove any character or set of characters at the beginning or end of a string.

Syntax
Function Overload 1
public string Trim()

This is the primary trim function that doesn't take any parameters and it is understood that till remove leading and trailing whitespace are. See the Example below.

string userInput = " SomeName LastName ";//User input with some white space at the beginning and end.
string trimmedOutput = userInput.Trim();//Call trim function with no parameter to remove the white space
Console.WriteLine(trimmedOutput);//Write to screen
Output
SomeName LastName

This code snippet removes the spaces at the beginning and end of the string but preserves the space in the middle of the string.

Function Overload 2
public string Trim(char trimChar)

This overload allows the user to provide a character to remove at the beginning and end. If some text had special characters that we need to remove this would be good to use for that. Example below.

string userInput = "#CSharpDevelopers!";//User input with two special characters 
string trimmedOutput = userInput.Trim('#');//Call trim function with # has a removal input
Console.WriteLine(trimmedOutput);//Write to screen
Output
CSharpDevelopers!

As you can see if the input was a hashtag the hashtag could be removed by using the trim function and passing a # character. This case had two special characters hashtag and an exclamation point but only the hashtag is removed.

Function Overload 3
public string Trim(char[] trimChars)

This last version of the function is the most flexible as an array of different characters can be provided to remove from the before and after the string. See the example below.

string userInput = "#CSharpDevelopers!";//User input with two special characters 
string trimmedOutput = userInput.Trim(new char[] { '#', '!' });//Call trim function with #,! has a removal input
Console.WriteLine(trimmedOutput);//Write to screen
Output
CSharpDevelopers

This trim function has removed both the inputs '#' in the beginning and also '!' at the end.

Exceptions

There are no exceptions that can come from this function because the input is only a char or array of characters. Even if null is passed no exception is thrown.

When Might This Function Be Used?

This function is great to trim unwanted text when there are single characters to remove like white space, tabs, special characters, or returns. It is typically used to quickly remove white space. This can be from user input from the UI, or from a database.

Alternative Methods of Trim Function

One way to remove unwanted characters is to use string replace, but you have to be sure that the characters that you want to remove are only at the beginning and end of the string. Since this method scans the entire string and not just the leading and ending characters it could potentially remove text that you didn't want to. There is an example below.

string userInput = "#CSharpDevelopers!";//User input with two special characters 
string trimmedOutput = userInput.Replace("#", "");//replace all instances of #
trimmedOutput = trimmedOutput.Replace("!", "");
Console.WriteLine(trimmedOutput);//Write to screen
Output
CSharpDevelopers

How does String Trim Work?

The are many ways to approach writing a trim function. The text is looped through twice. In the first pass, the text is grouped into whitespace and non-whitespace. The grouping of whitespace is considered to have the potential to be trimmed, but not all whitespace may be trimmed if it is in between text. Then each grouping is checked to see if it is at the beginning or the end and also has the potential to be trimmed. See the example below.

string userInput = " SimpleFirstName MiddleName SimpleLastName ";//User input with some white space at the beginning and end.
string trimmedOutput = TrimText(userInput);//Custom Trim function with no parameter to remove the white space
Console.WriteLine(trimmedOutput);//Write to screen

string TrimText(string originalText)
{
    string newText = "";//Start with an empty string
    Dictionary<int, Group> textGroupedDict = GetFirstPass(originalText);//Group the text between whitespace and non whitespace
    foreach (int groupIndex in textGroupedDict.Keys)//Loop through all groups
    {
        Group textGroup = textGroupedDict[groupIndex];//Select the group
        if (textGroup.IsTrimPossible && groupIndex == 0)
        {
            //If the group is at the beginning and flagged as possible to trim then do not add to the final text
        }
        else if (textGroup.IsTrimPossible && groupIndex == textGroupedDict.Count - 1)
        {
            //If the group is at the end and flagged as possible to trim then do not add to the final text
        }
        else
        {
            newText += textGroup.GroupText;//Add all text that is not at the beginning and end and is flagged to be trimmed can be added
        }
    }
    return newText;
}

Dictionary<int, Group> GetFirstPass(string originalText)
{
    int groupCount = 0;//Number assigned to each group
    Dictionary<int, Group> groupDict = new Dictionary<int, Group>();//Group of text
    bool isPreviewWhiteSpace = false;//Track if the previous character was whitespace or not
    int index = 0;//Index of the characters
    foreach (char letter in originalText)
    {
        bool isCurrentWhiteSpace = false;
        if (char.IsWhiteSpace(letter))
        {
            isCurrentWhiteSpace = true;
        }
        else
        {
            isCurrentWhiteSpace = false;
        }
        if ((isCurrentWhiteSpace == isPreviewWhiteSpace) || (isCurrentWhiteSpace && index == 0))//If current and previous is whitespace or whitespace is at the beginning to flag as a potential for trimming
        {
            AddToGroupDict(groupDict, groupCount, letter, true);
        }
        else if (isCurrentWhiteSpace == true && isPreviewWhiteSpace == false)//If the current is whitespace and the previous was not then flag as a potential for trimming
        {
            groupCount++;
            AddToGroupDict(groupDict, groupCount, letter, true);
        }
        else // all other cases at to a group but do not flag for trimming
        {
            groupCount++;
            AddToGroupDict(groupDict, groupCount, letter, false);
        }
        isPreviewWhiteSpace = isCurrentWhiteSpace;
        index++;
    }
    return groupDict;
}

void AddToGroupDict(Dictionary<int, Group> groupDict, int groupCount, char letter, bool isTrimPossible)
{
    if (!groupDict.ContainsKey(groupCount))//If the groupCount does not exist then create a new group
    {
        Group group = new Group(groupCount, letter, isTrimPossible);
        groupDict[groupCount] = group;
    }
    else //If the group count exists already then add the letters and index to the dict
    {
        groupDict[groupCount].AddCharacter(letter);
    }
}

class Group
{
    public Group(int groupId, char letter, bool isTrimPossible)
    {
        GroupText = "";
        GroupId = groupId;
        IsTrimPossible = isTrimPossible;
        AddCharacter(letter);
    }
    public int GroupId { get; set; } //Unique dictionary identifier 
    public string GroupText { get; set; } //Store all characters in a string that belongs to the group
    public bool IsTrimPossible { get; set; } //Flag for trimming in the second pass
    public override bool Equals(object? obj)
    {
        return obj is Group group &&
                GroupId == group.GroupId;
    }
    public override int GetHashCode()
    {
        return HashCode.Combine(GroupId);
    }
    public void AddCharacter(char letter)
    {
        GroupText += letter;
    }
}
Output
SimpleFirstName Middle SimpleLastName

The white space was trimmed from the beginning and the end of this sample code.

What if I don't want to trim the whitespace but a character? Then I need to do something similar to the previous example but instead of looking for whitespace, it looks for a character. See the full code example below.

string userInput = "#SimpleHasTagName!";//User input with some white space at the beginning and end.
string trimmedOutput = TrimText(userInput,'#');//Custom Trim function with no parameter to remove the white space
Console.WriteLine(trimmedOutput);//Write to screen
string TrimText(string originalText, char charToTrim)
{
    string newText = "";//Start with an empty string
    Dictionary<int, Group> textGroupedDict = GetFirstPass(originalText, charToTrim);//Group the text between whitespace and non whitespace
    foreach (int groupIndex in textGroupedDict.Keys)//Loop through all groups
    {
        Group textGroup = textGroupedDict[groupIndex];//Select the group
        if (textGroup.IsTrimPossible && groupIndex == 0)
        {
            //If the group is at the beginning and flagged as possible to trim then do not add to the final text
        }
        else if (textGroup.IsTrimPossible && groupIndex == textGroupedDict.Count - 1)
        {
            //If the group is at the end and flagged as possible to trim then do not add to the final text
        }
        else
        {
            newText += textGroup.GroupText;//Add all text that is not at the beginning and end and is flagged to be trimmed can be added
        }
    }
    return newText;
}

Dictionary<int, Group> GetFirstPass(string originalText,char charToTrim)
{
    int groupCount = 0;//Number assigned to each group
    Dictionary<int, Group> groupDict = new Dictionary<int, Group>();//Group of text
    bool isPreviousCharToTrim = false;//Track if the previous character was a character to trim  or not
    int index = 0;//Index of the characters
    foreach (char letter in originalText)
    {
        bool isCurrentCharToTrim = false;
        if (letter == charToTrim)
        {
            isCurrentCharToTrim = true;
        }
        else
        {
            isCurrentCharToTrim = false;
        }
        if ((isCurrentCharToTrim == isPreviousCharToTrim) || (isCurrentCharToTrim && index == 0))//If current and previous is a character to trim or a character to trim is at the beginning to flag as a potential for trimming
        {
            AddToGroupDict(groupDict, groupCount, letter, true);
        }
        else if (isCurrentCharToTrim == true && isPreviousCharToTrim == false)//If the current is a character to trim  and the previous was not then flag as a potential for trimming
        {
            groupCount++;
            AddToGroupDict(groupDict, groupCount, letter, true);
        }
        else // all other cases at to a group but do not flag for trimming
        {
            groupCount++;
            AddToGroupDict(groupDict, groupCount, letter, false);
        }
        isPreviousCharToTrim = isCurrentCharToTrim;
        index++;
    }
    return groupDict;
}

void AddToGroupDict(Dictionary<int, Group> groupDict, int groupCount, char letter, bool isTrimPossible)
{
    if (!groupDict.ContainsKey(groupCount))//If the groupCount does not exist then create a new group
    {
        Group group = new Group(groupCount, letter, isTrimPossible);
        groupDict[groupCount] = group;
    }
    else //If the groupCount exists already then add the letters and index to the 
    {
        groupDict[groupCount].AddCharacter(letter);
    }
}

class Group
{
    public Group(int groupId, char letter, bool isTrimPossible)
    {
        GroupText = "";
        GroupId = groupId;
        IsTrimPossible = isTrimPossible;
        AddCharacter(letter);
    }
    public int GroupId { get; set; } //Unique dictionary identifier 
    public string GroupText { get; set; } //Store all characters in a string that belongs to the group
    public bool IsTrimPossible { get; set; } //Flag for trimming in the second pass
    public override bool Equals(object? obj)
    {
        return obj is Group group &&
                GroupId == group.GroupId;
    }
    public override int GetHashCode()
    {
        return HashCode.Combine(GroupId);
    }
    public void AddCharacter(char letter)
    {
        GroupText += letter;
    }
}
Output
SimpleHasTagName!

We see that the hashtag was trimmed from the beginning and nothing else was trimmed. Next, let's take a look at an example where we can pass an array of characters to the trimming function.

string userInput = "#SimpleHasTagName!";//User input with some white space at the beginning and end.
string trimmedOutput = TrimText(userInput, new char[] { '#', '!' });//Custom Trim function with no parameter to remove the white space
Console.WriteLine(trimmedOutput);//Write to screen

string TrimText(string originalText, char[] charsToTrim)
{
    string newText = "";//Start with an empty string
    Dictionary<int, Group> textGroupedDict = GetFirstPass(originalText, charsToTrim);//Group the text between whitespace and non whitespace
    foreach (int groupIndex in textGroupedDict.Keys)//Loop through all groups
    {
        Group textGroup = textGroupedDict[groupIndex];//Select the group
        if (textGroup.IsTrimPossible && groupIndex == 0)
        {
            //If the group is at the beginning and flagged as possible to trim then do not add to the final text
        }
        else if (textGroup.IsTrimPossible && groupIndex == textGroupedDict.Count - 1)
        {
            //If the group is at the end and flagged as possible to trim then do not add to the final text
        }
        else
        {
            newText += textGroup.GroupText;//Add all text that is not at the begginning and end and is flagged to be trimmed can be added
        }
    }
    return newText;
}

Dictionary<int, Group> GetFirstPass(string originalText, char[] charsToTrim)
{
    int groupCount = 0;//Number assigned to each group
    Dictionary<int, Group> groupDict = new Dictionary<int, Group>();//Group of text
    bool isPreviousCharToTrim = false;//Track if the previous character was a character to trim  or not
    int index = 0;//Index of the characters
    foreach (char letter in originalText)
    {
        bool isCurrentCharToTrim = false;
        foreach (char charToTrim in charsToTrim)//added a loop to check if the current letter has any of the characters to trim
        {
            if (letter == charToTrim)
            {
                isCurrentCharToTrim = true;
                break;//If we find any characters to trim then exit the loop
            }
            else
            {
                isCurrentCharToTrim = false;
            }
        }
        if ((isCurrentCharToTrim == isPreviousCharToTrim) || (isCurrentCharToTrim && index == 0))//If current and previous is a character to trim or a character to trim is at the beginning to flag as a potential for trimming
        {
            AddToGroupDict(groupDict, groupCount, letter, true);
        }
        else if (isCurrentCharToTrim == true && isPreviousCharToTrim == false)//If the current is a character to trim  and the previous was not then flag as a potential for trimming
        {
            groupCount++;
            AddToGroupDict(groupDict, groupCount, letter, true);
        }
        else // all other cases at to a group but do not flag for trimming
        {
            groupCount++;
            AddToGroupDict(groupDict, groupCount, letter, false);
        }
        isPreviousCharToTrim = isCurrentCharToTrim;
        index++;
    }
    return groupDict;
}

void AddToGroupDict(Dictionary<int, Group> groupDict, int groupCount, char letter, bool isTrimPossible)
{
    if (!groupDict.ContainsKey(groupCount))//If the groupCount does not exist then create a new group
    {
        Group group = new Group(groupCount, letter, isTrimPossible);
        groupDict[groupCount] = group;
    }
    else //If the groupCount exists already then add the letters and index to the 
    {
        groupDict[groupCount].AddCharacter(letter);
    }
}

class Group
{
    public Group(int groupId, char letter, bool isTrimPossible)
    {
        GroupText = "";
        GroupId = groupId;
        IsTrimPossible = isTrimPossible;
        AddCharacter(letter);
    }
    public int GroupId { get; set; } //Unique dictionary identifier 
    public string GroupText { get; set; } //Store all characters in a string that belongs to the group
    public bool IsTrimPossible { get; set; } //Flag for trimming in the second pass
    public override bool Equals(object? obj)
    {
        return obj is Group group &&
               GroupId == group.GroupId;
    }
    public override int GetHashCode()
    {
        return HashCode.Combine(GroupId);
    }
    public void AddCharacter(char letter)
    {
        GroupText += letter;
    }
}

Output

SimpleHasTagName

This updated sample code trims multiple characters from beginning and end just like the native csharp examples.

Get Latest Updates