// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved.
// This code can only be used under the standard Unity Asset Store End User License Agreement
// A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
using System;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using static System.String;
namespace Doozy.Runtime.Common.Extensions
{
/// Extension methods for the string (String) class
public static class StringExtensions
{
/// Wrap the given text in a color tag for the given color
/// Text to colorize
/// Color
/// Text wrapped in a color tag
public static string Colorize(this string text, Color color) =>
$"{text}";
/// Wrap in Bold tags the given text
/// Text to wrap
/// Text wrapped in Bold tags
public static string Bold(this string text) =>
$"{text}";
/// Wrap in Italic tags the given text
/// Text to wrap
/// Text wrapped in Italic tags
public static string Italic(this string text) =>
$"{text}";
/// Remove all whitespaces from string
/// Target string
/// Processed string
public static string RemoveWhitespaces(this string target) =>
Regex.Replace(target, @"\s+", "");
///
/// Remove any numbers from the beginning of the string and all special characters.
/// This is needed to make sure we can use the string to generate enum names, which cannot start with a number or contain special characters.
///
/// Target string
/// Processed string
public static string CleanName(this string target)
{
target =
target
.RemoveWhitespaces()
.RemoveAllSpecialCharacters();
while (target.Length > 0 && !char.IsLetter(target[0]))
target = target.Remove(0, 1);
return target;
}
/// Convert all whitespaces to single space
/// Target string
/// Processed string
public static string ConvertWhitespacesToSingleSpaces(this string target) =>
Regex.Replace(target, @"\s+", " ");
/// Reverse back or forward slashes
/// String value
/// 0 - replace forward slash with back, 1 - replace back with forward slash
/// Processed string
public static string ReverseSlash(this string target, int direction)
{
switch (direction)
{
case 0:
return target.Replace(@"/", @"\");
case 1:
return target.Replace(@"\", @"/");
default:
return target;
}
}
///
/// Get the left part of the string, up until the character c.
/// If c is not found in the string the whole string is returned.
///
/// String value to truncate
/// Separator
/// Processed string
public static string LeftOf(this string target, char c)
{
int ndx = target.IndexOf(c);
return ndx >= 0
? target.Substring(0, ndx)
: target;
}
///
/// Get the right part of the string, after the character c.
/// If c is not found in the string the whole string is returned.
///
/// String value to truncate
/// Separator
/// Processed string
public static string RightOf(this string target, char c)
{
int ndx = target.IndexOf(c);
return ndx == -1
? target
: target.Substring(ndx + 1);
}
/// Remove the last character from a string
/// String value
/// Processed string
public static string RemoveLastCharacter(this string target) =>
target.Length > 0 ? target.Substring(0, target.Length - 1) : target;
/// Remove the last number of characters from a string
/// String value
/// Number of characters to remove from the end of the target string
/// Processed string
public static string RemoveLast(this string target, int numberOfCharactersToRemove) =>
target.IsNullOrEmpty() ? string.Empty : target.Substring(0, target.Length - numberOfCharactersToRemove);
/// Remove the first character from a string
/// String value
/// Processed string
public static string RemoveFirstCharacter(this string target) =>
target.Substring(1);
/// Remove the first number of characters from a string
/// String value
/// Number of characters to remove from the start of the target string
/// Processed string
public static string RemoveFirst(this string target, int numberOfCharactersToRemove) =>
target.Substring(numberOfCharactersToRemove);
/// Remove all special characters from the string
/// String value
/// The adjusted string
/// Processed string
public static string RemoveAllSpecialCharacters(this string target)
{
var sb = new StringBuilder(target.Length);
foreach (char c in target.Where(char.IsLetterOrDigit)) sb.Append(c);
return sb.ToString();
}
///
/// Remove all empty lines from a formatted string
///
///
/// String value
/// Processed string
public static string RemoveAllEmptyLines(this string target) =>
Regex.Replace(target, @"^\s*$\n|\r", Empty, RegexOptions.Multiline).TrimEnd();
/// Replace Line Feeds
/// String value to remove line feeds from
/// Processed string
public static string ReplaceLineFeeds(this string target) =>
Regex.Replace(target, @"^[\r\n]+|\.|[\r\n]+$", "");
/// Check string is null
/// String to evaluate
/// True if string is null else false
public static bool IsNull(this string target) =>
target == null;
/// Check if string is null or empty
/// String to evaluate
/// True if string is null or is empty else false
public static bool IsNullOrEmpty(this string target) =>
string.IsNullOrEmpty(target);
///
/// Check if string length is a certain minimum number of characters.
/// Does not ignore leading and trailing white-space.
/// null strings will always evaluate to false.
///
/// String value to evaluate minimum length
/// Minimum allowable string length
/// True if string is of specified minimum length
public static bool IsMinLength(this string target, int minCharLength) =>
target != null &&
target.Length >= minCharLength;
///
/// Check if string length is consists of specified allowable maximum char length.
/// Does not ignore leading and trailing white-space.
/// null strings will always evaluate to false.
///
/// String value to evaluate maximum length
/// Maximum allowable string length
/// True if string has specified maximum char length
public static bool IsMaxLength(this string target, int maxCharLength) =>
target != null &&
target.Length <= maxCharLength;
///
/// Check if string length satisfies minimum and maximum allowable char length.
/// Does not ignore leading and trailing white-space.
///
/// String value to evaluate
/// Minimum char length
/// Maximum char length
/// True if string satisfies minimum and maximum allowable length
public static bool IsLength(this string target, int minCharLength, int maxCharLength) =>
target != null &&
target.Length >= minCharLength &&
target.Length <= minCharLength;
/// Get the number of characters in string checks if string is null
/// String to evaluate length of
/// Total number of chars or null if string is null
public static int? GetLength(string target) =>
target?.Length;
/// Extract the left part of the input string limited with the length parameter
/// The input string to take the left part from
/// The total number characters to take from the input string
/// The substring starting at startIndex 0 until length
/// Input is null
/// Length is smaller than zero or higher than the length of input
public static string Left(this string target, int length) =>
String.IsNullOrEmpty(target)
? throw new ArgumentNullException(nameof(target))
: length < 0 || length > target.Length
? throw new ArgumentOutOfRangeException(nameof(length), "Length cannot be higher than total string length or less than 0")
: target.Substring(0, length);
/// Extract the right part of the input string limited with the length parameter
/// The input string to take the right part from
/// The total number characters to take from the input string
/// The substring taken from the input string
/// Input is null
/// Length is smaller than zero or higher than the length of input
public static string Right(this string target, int length) =>
String.IsNullOrEmpty(target)
? throw new ArgumentNullException(nameof(target))
: length < 0 || length > target.Length
? throw new ArgumentOutOfRangeException(nameof(length), "Length cannot be higher than total string length or less than 0")
: target.Substring(target.Length - length);
/// Check if a string does not start with prefix
/// String value to evaluate
/// Prefix
/// True if string does not match prefix else false, null values will always evaluate to false
public static bool DoesNotStartWith(this string target, string prefix) =>
target == null ||
prefix == null ||
!target.StartsWith(prefix, StringComparison.InvariantCulture);
/// Check if a string does not end with prefix
/// String value to evaluate
/// Suffix
/// True if string does not match prefix else false, null values will always evaluate to false
public static bool DoesNotEndWith(this string target, string suffix) =>
target == null ||
suffix == null ||
!target.EndsWith(suffix, StringComparison.InvariantCulture);
/// Remove the first part of the string, if no match found return original string
/// String value to remove prefix from
/// Prefix
/// Indicates whether the compare should ignore case
/// Trimmed string with no prefix or original string
public static string RemovePrefix(this string target, string prefix, bool ignoreCase = true) =>
!String.IsNullOrEmpty(target) && (ignoreCase
? target.StartsWithIgnoreCase(prefix)
: target.StartsWith(prefix))
? target.Substring(prefix.Length, target.Length - prefix.Length)
: target;
/// Remove the end part of the string, if no match found return original string
/// String value to remove suffix from
/// Suffix
/// Indicates whether the compare should ignore case
/// Trimmed string with no suffix or original string
public static string RemoveSuffix(this string target, string suffix, bool ignoreCase = true) =>
!String.IsNullOrEmpty(target) && (ignoreCase
? target.EndsWithIgnoreCase(suffix)
: target.EndsWith(suffix))
? target.Substring(0, target.Length - suffix.Length)
: Empty;
/// Append the suffix to the end of the string if the string does not already end in the suffix
/// String value to append suffix to
/// Suffix
/// Indicates whether the compare should ignore case
public static string AppendSuffixIfMissing(this string target, string suffix, bool ignoreCase = true) =>
String.IsNullOrEmpty(target) || (ignoreCase
? target.EndsWithIgnoreCase(suffix)
: target.EndsWith(suffix))
? target
: target + suffix;
/// Append the prefix to the start of the string if the string does not already start with prefix
/// String value to append prefix to
/// Prefix
/// Indicates whether the compare should ignore case
public static string AppendPrefixIfMissing(this string target, string prefix, bool ignoreCase = true) =>
String.IsNullOrEmpty(target) || (ignoreCase
? target.StartsWithIgnoreCase(prefix)
: target.StartsWith(prefix))
? target
: prefix + target;
///
/// Read in a sequence of words from standard input and capitalize each
/// one (make first letter uppercase; make rest lowercase).
///
/// String value
/// Word with capitalization
public static string Capitalize(this string target) =>
target.Length == 0 ? target : target.Substring(0, 1).ToUpper() + target.Substring(1).ToLower();
/// Get the first character in string
/// String value
/// System.string
public static string FirstCharacter(this string target) =>
!String.IsNullOrEmpty(target)
? target.Length >= 1
? target.Substring(0, 1)
: target
: null;
/// Get the last character in string
/// String value
/// System.string
public static string LastCharacter(this string target) =>
!String.IsNullOrEmpty(target)
? target.Length >= 1
? target.Substring(target.Length - 1, 1)
: target
: null;
/// Check if a string ends with another string ignoring the case
/// String value
/// Suffix
/// True or False
public static bool EndsWithIgnoreCase(this string target, string suffix) =>
target == null
? throw new ArgumentNullException(nameof(target), "Target parameter is null")
: suffix == null
? throw new ArgumentNullException(nameof(suffix), "Suffix parameter is null")
: target.Length >= suffix.Length && target.EndsWith(suffix, StringComparison.InvariantCultureIgnoreCase);
/// Check if a string starts with another string ignoring the case
/// String value
/// Prefix
/// True or False
public static bool StartsWithIgnoreCase(this string target, string prefix) =>
target == null
? throw new ArgumentNullException(nameof(target), "Target parameter is null")
: prefix == null
? throw new ArgumentNullException(nameof(prefix), "Prefix parameter is null")
: target.Length >= prefix.Length && target.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase);
/// Replace specified characters with an empty string
/// String value
/// List of characters to replace from the string
///
/// string s = "Friends";
/// s = s.Replace('F', 'r','i','s'); //s becomes 'end;
///
/// System.string
public static string Replace(this string target, params char[] chars) =>
chars.Aggregate(target, (current, c) => current.Replace(c.ToString(CultureInfo.InvariantCulture), ""));
/// Remove Characters from string
/// String value
/// List of characters to remove from the string
/// System.string
public static string RemoveChars(this string target, params char[] chars)
{
var sb = new StringBuilder(target.Length);
foreach (char c in target.Where(c => !chars.Contains(c))) sb.Append(c);
return sb.ToString();
}
/// Validate email address
/// String email address
/// True or False
public static bool IsEmailAddress(this string target)
{
const string pattern = "^[a-zA-Z][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$";
return Regex.Match(target, pattern).Success;
}
/// Reverse string
/// String value
/// System.string
public static string Reverse(this string target)
{
char[] chars = new char[target.Length];
for (int i = target.Length - 1, j = 0; i >= 0; --i, ++j) chars[j] = target[i];
target = new string(chars);
return target;
}
/// Count number of occurrences in string
/// String value containing text
/// String or pattern find
/// int
public static int CountOccurrences(this string target, string stringToMatch) =>
Regex.Matches(target, stringToMatch, RegexOptions.IgnoreCase).Count;
///
/// Check if the String contains only Unicode letters.
/// null will return false. An empty String ("") will return false.
///
/// String value to check if is Alpha
/// True if only contains letters, and is non-null
public static bool IsAlpha(this string target) =>
!String.IsNullOrEmpty(target) && target.Trim()
.Replace(" ", "")
.All(char.IsLetter);
///
/// Check if the String contains only Unicode letters, digits.
/// null will return false. An empty String ("") will return false.
///
/// String value to check if is Alpha or Numeric
public static bool IsAlphaNumeric(this string target) =>
!String.IsNullOrEmpty(target) && target.Trim()
.Replace(" ", "")
.All(char.IsLetterOrDigit);
///
/// Encrypt a string using the supplied key.
/// Encoding is done using RSA encryption.
///
/// String value that must be encrypted
/// Encryption key
/// A string representing a byte array separated by a minus sign
// /// Occurs when stringToEncrypt or key is null or empty
public static string Encrypt(this string target, string key)
{
var cspParameter = new CspParameters { KeyContainerName = key };
var rsaServiceProvider = new RSACryptoServiceProvider(cspParameter) { PersistKeyInCsp = true };
byte[] bytes = rsaServiceProvider.Encrypt(Encoding.UTF8.GetBytes(target), true);
return BitConverter.ToString(bytes);
}
///
/// Decrypt a string using the supplied key.
/// Decoding is done using RSA encryption.
///
/// String value that must be decrypted
/// Decryption key
/// The decrypted string or null if decryption failed
// /// Occurs when stringToDecrypt or key is null or empty
public static string Decrypt(this string target, string key)
{
var cspParameters = new CspParameters { KeyContainerName = key };
var rsaServiceProvider = new RSACryptoServiceProvider(cspParameters) { PersistKeyInCsp = true };
string[] decryptArray = target.Split(new[] { "-" }, StringSplitOptions.None);
byte[] decryptByteArray = Array.ConvertAll(decryptArray, s => Convert.ToByte(byte.Parse(s, NumberStyles.HexNumber)));
byte[] bytes = rsaServiceProvider.Decrypt(decryptByteArray, true);
string result = Encoding.UTF8.GetString(bytes);
return result;
}
/// Calculate the amount of bytes occupied by the input string encoded as the encoding specified
/// The input string to check
/// The encoding to use
/// The total size of the input string in bytes
/// Input is null
/// Encoding is null
public static int GetByteSize(this string target, Encoding encoding) =>
target == null
? throw new ArgumentNullException(nameof(target))
: encoding == null
? throw new ArgumentNullException(nameof(encoding))
: encoding.GetByteCount(target);
}
}