Skip to content

Instantly share code, notes, and snippets.

@otuncelli
Created March 17, 2025 02:58
Show Gist options
  • Save otuncelli/85ceed657e7efe2623cac43893420f9a to your computer and use it in GitHub Desktop.
Save otuncelli/85ceed657e7efe2623cac43893420f9a to your computer and use it in GitHub Desktop.
The string.Trim() implementation but removes both white-space and the provided characters in one pass.
using System.Runtime.CompilerServices;
namespace BestProgram;
internal static class StringTrimExtensions
{
#region TrimStartWhiteSpaceAnd
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string TrimStartWhiteSpaceAnd(this string s, char trimChar)
{
return s.TrimWhiteSpaceAnd(trimChar, TrimType.Head);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string TrimStartWhiteSpaceAnd(this string s, params HashSet<char> trimChars)
{
return s.TrimWhiteSpaceAnd(TrimType.Head, trimChars);
}
#endregion
#region TrimEndWhiteSpaceAnd
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string TrimEndWhiteSpaceAnd(this string s, char trimChar)
{
return s.TrimWhiteSpaceAnd(trimChar, TrimType.Tail);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string TrimEndWhiteSpaceAnd(this string s, params HashSet<char> trimChars)
{
return s.TrimWhiteSpaceAnd(TrimType.Tail, trimChars);
}
#endregion
#region TrimWhiteSpaceAnd
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string TrimWhiteSpaceAnd(this string s, char trimChar)
{
return s.TrimWhiteSpaceAnd(trimChar, TrimType.Both);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string TrimWhiteSpaceAnd(this string s, params HashSet<char> trimChars)
{
return s.TrimWhiteSpaceAnd(TrimType.Both, trimChars);
}
#endregion
#region Private
private static string TrimWhiteSpaceAnd(this string s, char trimChar, TrimType trimType)
{
ArgumentNullException.ThrowIfNull(s);
if (s.Length == 0)
{
return string.Empty.Trim();
}
ReadOnlySpan<char> span = s.AsSpan();
int start = 0;
int end = span.Length - 1;
char ch;
if (trimType.HasFlag(TrimType.Head))
{
while (start < span.Length)
{
ch = span[start];
if (!char.IsWhiteSpace(ch) && ch != trimChar)
{
break;
}
start++;
}
}
if (trimType.HasFlag(TrimType.Tail))
{
while (end >= start)
{
ch = span[end];
if (!char.IsWhiteSpace(ch) && ch != trimChar)
{
break;
}
end--;
}
}
int length = end - start + 1;
return length == span.Length ? s : CreateTrimmedString(span, start, length);
}
private static string TrimWhiteSpaceAnd(this string s, TrimType trimType, params HashSet<char> chars)
{
ArgumentNullException.ThrowIfNull(s);
if (s.Length == 0 || chars.Count <= 0)
{
return s.Trim();
}
ReadOnlySpan<char> span = s.AsSpan();
int start = 0;
int end = span.Length - 1;
char ch;
if (trimType.HasFlag(TrimType.Head))
{
while (start < span.Length)
{
ch = span[start];
if (!char.IsWhiteSpace(ch) && !chars.Contains(ch))
{
break;
}
start++;
}
}
if (trimType.HasFlag(TrimType.Tail))
{
while (end >= start)
{
ch = span[end];
if (!char.IsWhiteSpace(ch) && !chars.Contains(ch))
{
break;
}
end--;
}
}
int length = end - start + 1;
return length == span.Length ? s : CreateTrimmedString(span, start, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string CreateTrimmedString(ReadOnlySpan<char> span, int start, int length)
{
return length <= 0 ? string.Empty : new string(span.Slice(start, length));
}
[Flags]
private enum TrimType
{
Head = 1,
Tail = 2,
Both = 3
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment