Click here to Skip to main content
15,879,239 members
Articles / Programming Languages / C#
Alternative
Tip/Trick

StringBuilder Extensions

Rate me:
Please Sign up or sign in to vote.
4.71/5 (13 votes)
9 Nov 2016CPOL 18.7K   13   12
An alternative set of StringBuilder Extension methods that is closer to the methods of System.String.

Introduction

These extension methods are adapted from those recently added to the LoycCore libraries. This set is more similar to the methods of System.String than the original article was. Not all methods of string are included - this set focuses more on searching than changing the string. LINQ-like O(1) Last() and LastOrDefault() methods are also included.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Loyc
{
    /// <summary>Extension methods that add some functionality of <c>string</c> to 
    /// <c>StringBuilder</c>.</summary>
    public static class StringBuilderExt
    {
        /// <summary>Extracts a substring from the specified StringBuiler.</summary>
        public static string Substring(this StringBuilder sb, int startIndex, int length)
        {
            return sb.ToString(startIndex, length);
        }

        /// <summary>Returns the last character of the string</summary>
        public static char Last(this StringBuilder str)
        {
            if (str.Length == 0)
                throw new InvalidOperationException("Empty string has no last character");
            return str[str.Length - 1];
        }

        /// <summary>Returns the last character of the string,
        /// or a default character if the string is empty.</summary>
        public static char LastOrDefault(this StringBuilder str, char @default = '\0')
        {
            if (str.Length == 0)
                return @default;
            return str[str.Length - 1];
        }

        static char[] _defaultTrimChars = new[] { ' ', '\t' };

        /// <summary>Removes all leading occurrences of spaces
        /// and tabs from the StringBuilder object.</summary>
        public static StringBuilder TrimStart(this StringBuilder sb)
            { return TrimStart(sb, _defaultTrimChars); }
        /// <summary>Removes all leading occurrences of the specified
        /// set of characters from the StringBuilder object.</summary>
        /// <param name="trimChars">An array of Unicode characters to remove.</param>
        public static StringBuilder TrimStart(this StringBuilder sb, params char[] trimChars)
        {
            int i = 0;
            while (i < sb.Length && trimChars.Contains(sb[i]))
                i++;
            sb.Remove(0, i);
            return sb;
        }

        /// <summary>Removes all trailing occurrences of spaces
        /// and tabs from the StringBuilder object.</summary>
        public static StringBuilder TrimEnd(this StringBuilder sb)
            { return TrimEnd(sb, _defaultTrimChars); }
        /// <summary>Removes all trailing occurrences of the specified
        /// set of characters from the StringBuilder object.</summary>
        /// <param name="trimChars">An array of Unicode characters to remove.</param>
        public static StringBuilder TrimEnd(this StringBuilder sb, params char[] trimChars)
        {
            int i = sb.Length;
            while (i > 0 && trimChars.Contains(sb[i - 1]))
                i--;
            sb.Remove(i, sb.Length - i);
            return sb;
        }

        /// <summary>Removes all leading and trailing occurrences of
        /// spaces and tabs from the StringBuilder object.</summary>
        public static StringBuilder Trim(this StringBuilder sb)
            { return Trim(sb, _defaultTrimChars); }
        /// <summary>Removes all leading and trailing occurrences of the
        /// specified set of characters from the StringBuilder object.</summary>
        /// <param name="trimChars">An array of Unicode characters to remove.</param>
        public static StringBuilder Trim(this StringBuilder sb, params char[] trimChars)
        {
            return TrimStart(TrimEnd(sb, trimChars), trimChars);
        }

        /// <summary>Gets the index of a character in a StringBuilder</summary>
        /// <returns>Index of the first instance of the specified
        /// character in the string, or -1 if not found</returns>
        public static int IndexOf(this StringBuilder sb, char value, int startIndex = 0)
        {
            for (int i = startIndex; i < sb.Length; i++)
                if (sb[i] == value)
                    return i;
            return -1;
        }

        /// <summary>Gets the index of a substring in a StringBuilder</summary>
        /// <returns>Index of the first instance of the specified
        /// substring in the StringBuilder, or -1 if not found</returns>
        public static int IndexOf(this StringBuilder sb, string searchStr, 
                                  int startIndex = 0, bool ignoreCase = false)
        {
            var stopAt = sb.Length - searchStr.Length;
            for (int i = startIndex; i <= stopAt; i++)
                if (SubstringEqualHelper(sb, i, searchStr, ignoreCase))
                    return i;
            return -1;
        }

        /// <summary>Gets the index of a character in a StringBuilder</summary>
        /// <returns>Index of the last instance of the specified
        /// character in the StringBuilder, or -1 if not found</returns>
        public static int LastIndexOf(this StringBuilder sb, char searchChar, 
                                      int startIndex = int.MaxValue)
        {
            if (startIndex > sb.Length - 1)
                startIndex = sb.Length - 1;
            for (int i = startIndex; i >= 0; i--)
                if (sb[i] == searchChar)
                    return i;
            return -1;
        }

        /// <summary>Gets the index of a substring in a StringBuilder</summary>
        /// <returns>Index of the last instance of the specified
        /// substring in the StringBuilder, or -1 if not found</returns>
        public static int LastIndexOf(this StringBuilder sb, 
            string searchStr, int startIndex = int.MaxValue, bool ignoreCase = false)
        {
            if (startIndex > sb.Length - searchStr.Length)
                startIndex = sb.Length - searchStr.Length;
            for (int i = startIndex; i >= 0; i--)
                if (SubstringEqualHelper(sb, i, searchStr, ignoreCase))
                    return i;
            return -1;
        }

        /// <summary>Finds out whether the StringBuilder ends
        /// with the specified substring.</summary>
        public static bool EndsWith(this StringBuilder sb, string what, bool ignoreCase = false)
        {
            if (what.Length > sb.Length)
                return false;
            return SubstringEqualHelper(sb, sb.Length - what.Length, what, ignoreCase);
        }

        /// <summary>Finds out whether the StringBuilder starts
        /// with the specified substring.</summary>
        public static bool StartsWith(this StringBuilder sb, string what, bool ignoreCase = false)
        {
            if (what.Length > sb.Length)
                return false;
            return SubstringEqualHelper(sb, 0, what, ignoreCase);
        }

        /// <summary>Checks if the sequences of characters <c>what</c> is equal to
        /// <c>sb.Substring(start, what.Length)</c>, without actually creating a
        /// substring object.</summary>
        public static bool SubstringEquals
             (StringBuilder sb, int start, string what, bool ignoreCase = false)
        {
            if (start < 0)
                throw new ArgumentException("Invalid starting index for comparison");
            if (start > sb.Length - what.Length)
                return false;
            return SubstringEqualHelper(sb, start, what, ignoreCase);
        }

        static bool SubstringEqualHelper
               (StringBuilder sb, int start, string what, bool ignoreCase = false)
        {
            if (ignoreCase)
                for (int i = 0; i < what.Length; i++) {
                    if (char.ToUpperInvariant(sb[start + i]) != char.ToUpperInvariant(what[i]))
                        return false;
                }
            else
                for (int i = 0; i < what.Length; i++) {
                    if (sb[start + i] != what[i])
                        return false;
                }
            return true;
        }
    }
}  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions

 
QuestionCheckParam.IsNotNegative(...) ? Pin
MuchToLearn10-Nov-16 23:31
MuchToLearn10-Nov-16 23:31 
AnswerRe: CheckParam.IsNotNegative(...) ? Pin
Qwertie11-Nov-16 3:08
Qwertie11-Nov-16 3:08 
GeneralRe: CheckParam.IsNotNegative(...) ? Pin
MuchToLearn11-Nov-16 5:38
MuchToLearn11-Nov-16 5:38 
QuestionWrong implementation? Pin
Benny S. Tordrup10-Nov-16 3:21
Benny S. Tordrup10-Nov-16 3:21 
BugRe: Wrong implementation? Pin
Roo Walker10-Nov-16 4:18
professionalRoo Walker10-Nov-16 4:18 
AnswerRe: Wrong implementation? Pin
jrobb22910-Nov-16 10:17
jrobb22910-Nov-16 10:17 
AnswerRe: Wrong implementation? Pin
Qwertie10-Nov-16 17:30
Qwertie10-Nov-16 17:30 
Trim() should call both, but you did lead me to notice a bug where the 1-arg overload of TrimEnd called TrimStart. Fixed.

GeneralRe: Wrong implementation? Pin
Benny S. Tordrup10-Nov-16 19:00
Benny S. Tordrup10-Nov-16 19:00 
GeneralRe: Wrong implementation? Pin
Qwertie10-Nov-16 22:08
Qwertie10-Nov-16 22:08 
GeneralMy vote of 5 Pin
Farhad Reza10-Nov-16 2:12
Farhad Reza10-Nov-16 2:12 
Question5ed Pin
Karthik_Mahalingam10-Nov-16 2:09
professionalKarthik_Mahalingam10-Nov-16 2:09 
GeneralMy vote of 5 Pin
Ravindra.P.C9-Nov-16 23:20
professionalRavindra.P.C9-Nov-16 23:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.