// 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); } }