Skip to content

Instantly share code, notes, and snippets.

@JakeSays
Created July 31, 2020 23:13
Show Gist options
  • Save JakeSays/2aba0c5af54ebe0fc08b768058020a35 to your computer and use it in GitHub Desktop.
Save JakeSays/2aba0c5af54ebe0fc08b768058020a35 to your computer and use it in GitHub Desktop.
String things
using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Annotations;
namespace Csx.Utility
{
[PublicAPI]
public static class StringExtensions
{
[ContractAnnotation("value:null => true")]
public static bool NotUseful(this string value) =>
string.IsNullOrEmpty(value) ||
string.IsNullOrWhiteSpace(value);
public static bool IsUseful(this string value) => !value.NotUseful();
[ContractAnnotation("value:null => true")]
public static bool IsNullOrEmpty(this string value) => string.IsNullOrEmpty(value);
[ContractAnnotation("value:null => false")]
public static bool NotNullOrEmpty(this string value) => !string.IsNullOrEmpty(value);
[ContractAnnotation("value:null => true")]
public static bool IsNullOrWhiteSpace(this string value) => string.IsNullOrWhiteSpace(value);
[ContractAnnotation("value:null => false")]
public static bool NotNullOrWhiteSpace(this string value) => !string.IsNullOrWhiteSpace(value);
//^ was return !string.IsNullOrEmpty(value);
public static string ValueOrEmpty(this string value) =>
string.IsNullOrEmpty(value)
? ""
: value;
public static string Flatten(this string[] values, string separator) =>
values == null
? ""
: string.Join(separator, values);
public static string ValueOrNull(this string value) =>
string.IsNullOrEmpty(value)
? null
: value;
public static string Last(this string value, int charCount)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
var len = value.Length;
return len <= charCount
? value
: value.Substring(len - charCount, charCount);
}
public static string[] Trim(this string[] value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
for (var idx = 0; idx < value.Length; idx++)
{
value[idx] = value[idx].SafeTrim();
}
return value;
}
public static string ValueOrDefault(this string value, string defaultValue) =>
string.IsNullOrEmpty(value)
? defaultValue
: value;
public static string SafeTrim(this string value) => value?.Trim();
public static string SafeToUpperInvariant(this string value) => value?.ToUpperInvariant();
public static string SafeToLowerInvariant(this string value) => value?.ToLowerInvariant();
public static string Substring(this string value, string leftDelimiter, string rightDelimiter)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
var startPos = 0;
var endPos = 0;
if (!IsNullOrEmpty(leftDelimiter))
{
startPos = value.IndexOf(leftDelimiter, StringComparison.InvariantCulture);
if (startPos == -1)
{
return null;
}
startPos += leftDelimiter.Length;
}
if (!IsNullOrEmpty(rightDelimiter))
{
endPos = value.IndexOf(rightDelimiter, StringComparison.InvariantCulture);
if (endPos == -1)
{
return null;
}
}
if (endPos == 0)
{
endPos = value.Length;
}
var substr = value.Substring(startPos, endPos - startPos);
return substr;
}
public static bool EqualsInvariant(this string lhs, string rhs) =>
string.Equals(lhs, rhs, StringComparison.InvariantCulture);
public static bool EqualsInvariantIgnoreCase(this string lhs, string rhs) =>
string.Equals(lhs, rhs, StringComparison.InvariantCultureIgnoreCase);
public static string RemovePrefix(this string src,
string prefix,
StringComparison comparison = StringComparison.Ordinal)
{
if (src == null)
{
return null;
}
if (IsNullOrEmpty(prefix))
{
throw new ArgumentNullException(nameof(prefix));
}
if (src == prefix)
{
return "";
}
return src.StartsWith(prefix, comparison)
? src.Substring(prefix.Length)
: src;
}
public static byte[] Chop(this byte[] src, int maxLength)
{
if (src == null)
{
return null;
}
if (src.Length <= maxLength)
{
return src;
}
var result = new byte[maxLength];
Buffer.BlockCopy(src, 0, result, 0, maxLength);
return result;
}
public static string Chop(this string src, int maxLength)
{
if (src == null)
{
return null;
}
if (maxLength < 0)
{
throw new ArgumentOutOfRangeException(nameof(maxLength));
}
if (src.Length == 0 ||
src.Length == maxLength)
{
return src;
}
return src.Substring(0, Math.Min(src.Length, maxLength));
}
public static string WordWrap(string text, int width)
{
//lifted from: http://www.blackbeltcoder.com/Articles/strings/implementing-word-wrap-in-email-messages
//and improved
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (width < 1)
{
throw new ArgumentOutOfRangeException(nameof(width));
}
int pos, next;
var sb = new StringBuilder();
// Parse each line of text
for (pos = 0; pos < text.Length; pos = next)
{
// Find end of line
var eol = text.IndexOf(System.Environment.NewLine, pos, StringComparison.Ordinal);
if (eol == -1)
{
next = eol = text.Length;
}
else
{
next = eol + System.Environment.NewLine.Length;
}
// Copy this line of text, breaking into smaller lines as needed
if (eol > pos)
{
do
{
var len = eol - pos;
if (len > width)
{
len = BreakLine(text, pos, width);
}
sb.Append(text, pos, len);
sb.Append(System.Environment.NewLine);
// Trim whitespace following break
pos += len;
while (pos < eol &&
char.IsWhiteSpace(text[pos]))
{
pos++;
}
} while (eol > pos);
}
else
{
sb.Append(System.Environment.NewLine); // Empty line
}
}
return sb.ToString();
}
private static int BreakLine(string text, int pos, int max)
{
// Find last whitespace in line
var i = max;
while (i >= 0 &&
!char.IsWhiteSpace(text[pos + i]))
{
i--;
}
// If no whitespace found, break at maximum length
if (i < 0)
{
return max;
}
// Find start of whitespace
while (i >= 0 &&
char.IsWhiteSpace(text[pos + i]))
{
i--;
}
// Return length of text before whitespace
return i + 1;
}
public static List<string> BreakIntoLines(string text, int width)
{
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (width < 1)
{
throw new ArgumentOutOfRangeException(nameof(width));
}
var pos = 0;
var eol = text.Length;
var lines = new List<string>();
do
{
var len = eol - pos;
if (len > width)
{
len = BreakLine(text, pos, width);
}
var line = text.Substring(pos, len);
lines.Add(line);
// Trim whitespace following break
pos += len;
while (pos < eol &&
char.IsWhiteSpace(text[pos]))
{
pos++;
}
} while (eol > pos);
return lines;
}
public static string ToPascalCase(this string source) => FormatName(source, true);
public static string ToCamelCase(this string source) => FormatName(source, false);
private static string FormatName(string source, bool isPascalCase)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (source.Length == 0)
{
return source;
}
var buffer = source.ToCharArray();
var first = false;
var needle = 0;
for (var idx = 0; idx < buffer.Length; idx++)
{
var c = buffer[idx];
if (char.IsLetterOrDigit(c))
{
if (first || needle == 0)
{
buffer[needle] = first || isPascalCase && needle == 0
? char.ToUpperInvariant(c)
: char.ToLowerInvariant(c);
}
else
{
if (isPascalCase)
{
buffer[needle] = char.IsUpper(c)
? c
: char.ToLowerInvariant(c);
}
else
{
buffer[needle] = c;
}
}
first = false;
needle += 1;
}
else if (char.IsWhiteSpace(c) ||
char.IsPunctuation(c))
{
first = true;
}
else
{
buffer[needle] = c;
needle += 1;
}
}
return new string(buffer, 0, needle);
}
//lifted from System.CommandLine
public static string ToKebabCase(this string value)
{
if (string.IsNullOrEmpty(value))
{
return value;
}
var kebab = new StringBuilder();
var i = 0;
var addDash = false;
// handles beginning of string, breaks on first letter or digit. addDash might be better named "canAddDash"
for (; i < value.Length; i++)
{
var ch = value[i];
if (!char.IsLetterOrDigit(ch))
{
continue;
}
addDash = !char.IsUpper(ch);
kebab.Append(char.ToLowerInvariant(ch));
i++;
break;
}
// reusing i, start at the same place
for (; i < value.Length; i++)
{
var ch = value[i];
if (char.IsUpper(ch))
{
if (addDash)
{
addDash = false;
kebab.Append('-');
}
kebab.Append(char.ToLowerInvariant(ch));
continue;
}
if (char.IsLetterOrDigit(ch))
{
addDash = true;
kebab.Append(ch);
continue;
}
//this coverts all non letter/digits to dash - specifically periods and underscores. Is this needed?
addDash = false;
kebab.Append('-');
}
return kebab.ToString();
}
private static readonly string[] EmptyStrings = new string[0];
public static string[] SplitOnFirst(this string strVal, char needle)
{
if (strVal == null)
{
return EmptyStrings;
}
var pos = strVal.IndexOf(needle);
return pos == -1
? new[] {strVal}
: new[] {strVal.Substring(0, pos), strVal.Substring(pos + 1)};
}
public static string[] SplitOnFirst(this string strVal, string needle)
{
if (strVal == null)
{
return EmptyStrings;
}
var pos = strVal.IndexOf(needle, StringComparison.Ordinal);
return pos == -1
? new[] {strVal}
: new[] {strVal.Substring(0, pos), strVal.Substring(pos + 1)};
}
public static (string FirstPart, string LastPart) SplitFirst(this string value, char needle)
{
var parts = SplitOnFirst(value, needle);
return parts.Length == 1
? (parts[0], null)
: (parts[0], parts[1]);
}
public static (string FirstPart, string LastPart) SplitFirst(this string value, string needle)
{
var parts = SplitOnFirst(value, needle);
return parts.Length == 1
? (parts[0], null)
: (parts[0], parts[1]);
}
public static (string FirstPart, string LastPart) SplitLast(this string value, char needle)
{
var parts = value.SplitOnLast(needle);
return parts.Length == 1
? (parts[0], null)
: (parts[0], parts[1]);
}
public static (string FirstPart, string LastPart) SplitLast(this string value, string needle)
{
var parts = value.SplitOnLast(needle);
return parts.Length == 1
? (parts[0], null)
: (parts[0], parts[1]);
}
public static string[] SplitOnLast(this string strVal, char needle)
{
if (strVal == null)
{
return new string[0];
}
var pos = strVal.LastIndexOf(needle);
return pos == -1
? new[] {strVal}
: new[] {strVal.Substring(0, pos), strVal.Substring(pos + 1)};
}
public static string[] SplitOnLast(this string strVal, string needle)
{
if (strVal == null)
{
return EmptyStrings;
}
var pos = strVal.LastIndexOf(needle, StringComparison.Ordinal);
return pos == -1
? new[] {strVal}
: new[] {strVal.Substring(0, pos), strVal.Substring(pos + 1)};
}
public static string[] SplitWithoutEmptyEntries(this string value, params char[] needles)
{
var parts = value?.Split(needles, StringSplitOptions.RemoveEmptyEntries) ?? EmptyStrings;
return parts;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment