using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace Unity.VisualScripting { public static class StringUtility { public static bool IsNullOrWhiteSpace(string s) { return s == null || s.Trim() == string.Empty; } public static string FallbackEmpty(string s, string fallback) { if (string.IsNullOrEmpty(s)) { s = fallback; } return s; } public static string FallbackWhitespace(string s, string fallback) { if (IsNullOrWhiteSpace(s)) { s = fallback; } return s; } public static void AppendLineFormat(this StringBuilder sb, string format, params object[] args) { sb.AppendFormat(format, args); sb.AppendLine(); } public static string ToSeparatedString(this IEnumerable enumerable, string separator) { return string.Join(separator, enumerable.Cast().Select(o => o?.ToString() ?? "(null)").ToArray()); } public static string ToCommaSeparatedString(this IEnumerable enumerable) { return ToSeparatedString(enumerable, ", "); } public static string ToLineSeparatedString(this IEnumerable enumerable) { return ToSeparatedString(enumerable, Environment.NewLine); } public static bool ContainsInsensitive(this string haystack, string needle) { return haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0; } public static IEnumerable AllIndexesOf(this string haystack, string needle) { if (string.IsNullOrEmpty(needle)) { yield break; } for (var index = 0; ; index += needle.Length) { index = haystack.IndexOf(needle, index, StringComparison.OrdinalIgnoreCase); if (index == -1) { break; } yield return index; } } public static string Filter(this string s, bool letters = true, bool numbers = true, bool whitespace = true, bool symbols = true, bool punctuation = true) { var sb = new StringBuilder(); foreach (var c in s) { if ((!letters && char.IsLetter(c)) || (!numbers && char.IsNumber(c)) || (!whitespace && char.IsWhiteSpace(c)) || (!symbols && char.IsSymbol(c)) || (!punctuation && char.IsPunctuation(c))) { continue; } sb.Append(c); } return sb.ToString(); } public static string FilterReplace(this string s, char replacement, bool merge, bool letters = true, bool numbers = true, bool whitespace = true, bool symbols = true, bool punctuation = true) { var sb = new StringBuilder(); var wasFiltered = false; foreach (var c in s) { if ((!letters && char.IsLetter(c)) || (!numbers && char.IsNumber(c)) || (!whitespace && char.IsWhiteSpace(c)) || (!symbols && char.IsSymbol(c)) || (!punctuation && char.IsPunctuation(c))) { if (!merge || !wasFiltered) { sb.Append(replacement); } wasFiltered = true; } else { sb.Append(c); wasFiltered = false; } } return sb.ToString(); } public static string Prettify(this string s) { return s.FirstCharacterToUpper().SplitWords(' '); } public static bool IsWordDelimiter(char c) { return char.IsWhiteSpace(c) || char.IsSymbol(c) || char.IsPunctuation(c); } public static bool IsWordBeginning(char? previous, char current, char? next) { var isFirst = previous == null; var isLast = next == null; var isLetter = char.IsLetter(current); var wasLetter = previous != null && char.IsLetter(previous.Value); var isNumber = char.IsNumber(current); var wasNumber = previous != null && char.IsNumber(previous.Value); var isUpper = char.IsUpper(current); var wasUpper = previous != null && char.IsUpper(previous.Value); var isDelimiter = IsWordDelimiter(current); var wasDelimiter = previous != null && IsWordDelimiter(previous.Value); var willBeLower = next != null && char.IsLower(next.Value); return (!isDelimiter && isFirst) || (!isDelimiter && wasDelimiter) || (isLetter && wasLetter && isUpper && !wasUpper) || // camelCase => camel_Case (isLetter && wasLetter && isUpper && wasUpper && !isLast && willBeLower) || // => ABBRWord => ABBR_Word (isNumber && wasLetter) || // Vector3 => Vector_3 (isLetter && wasNumber && isUpper && willBeLower); // Word1Word => Word_1_Word, Word1word => Word_1word } public static bool IsWordBeginning(string s, int index) { Ensure.That(nameof(index)).IsGte(index, 0); Ensure.That(nameof(index)).IsLt(index, s.Length); var previous = index > 0 ? s[index - 1] : (char?)null; var current = s[index]; var next = index < s.Length - 1 ? s[index + 1] : (char?)null; return IsWordBeginning(previous, current, next); } public static string SplitWords(this string s, char separator) { var sb = new StringBuilder(); for (var i = 0; i < s.Length; i++) { var c = s[i]; if (i > 0 && IsWordBeginning(s, i)) { sb.Append(separator); } sb.Append(c); } return sb.ToString(); } public static string RemoveConsecutiveCharacters(this string s, char c) { var sb = new StringBuilder(); var previous = '\0'; foreach (var current in s) { if (current != c || current != previous) { sb.Append(current); previous = current; } } return sb.ToString(); } public static string ReplaceMultiple(this string s, HashSet haystacks, char replacement) { Ensure.That(nameof(haystacks)).IsNotNull(haystacks); var sb = new StringBuilder(); foreach (var current in s) { if (haystacks.Contains(current)) { sb.Append(replacement); } else { sb.Append(current); } } return sb.ToString(); } public static string Truncate(this string value, int maxLength, string suffix = "...") { return value.Length <= maxLength ? value : value.Substring(0, maxLength) + suffix; } public static string TrimEnd(this string source, string value) { if (!source.EndsWith(value)) { return source; } return source.Remove(source.LastIndexOf(value)); } public static string TrimStart(this string source, string value) { if (!source.StartsWith(value)) { return source; } return source.Substring(value.Length); } public static string FirstCharacterToLower(this string s) { if (string.IsNullOrEmpty(s) || char.IsLower(s, 0)) { return s; } return char.ToLowerInvariant(s[0]) + s.Substring(1); } public static string FirstCharacterToUpper(this string s) { if (string.IsNullOrEmpty(s) || char.IsUpper(s, 0)) { return s; } return char.ToUpperInvariant(s[0]) + s.Substring(1); } public static string PartBefore(this string s, char c) { Ensure.That(nameof(s)).IsNotNull(s); var index = s.IndexOf(c); if (index > 0) { return s.Substring(0, index); } else { return s; } } public static string PartAfter(this string s, char c) { Ensure.That(nameof(s)).IsNotNull(s); var index = s.IndexOf(c); if (index > 0) { return s.Substring(index + 1); } else { return s; } } public static void PartsAround(this string s, char c, out string before, out string after) { Ensure.That(nameof(s)).IsNotNull(s); var index = s.IndexOf(c); if (index > 0) { before = s.Substring(0, index); after = s.Substring(index + 1); } else { before = s; after = null; } } // Faster equivalents for chars public static bool EndsWith(this string s, char c) { Ensure.That(nameof(s)).IsNotNull(s); return s[s.Length - 1] == c; } public static bool StartsWith(this string s, char c) { Ensure.That(nameof(s)).IsNotNull(s); return s[0] == c; } public static bool Contains(this string s, char c) { Ensure.That(nameof(s)).IsNotNull(s); for (int i = 0; i < s.Length; i++) { if (s[i] == c) { return true; } } return false; } public static string NullIfEmpty(this string s) { if (s == string.Empty) { return null; } else { return s; } } public static string ToBinaryString(this int value) { return Convert.ToString(value, 2).PadLeft(8, '0'); } public static string ToBinaryString(this long value) { return Convert.ToString(value, 2).PadLeft(16, '0'); } public static string ToBinaryString(this Enum value) { return Convert.ToString(Convert.ToInt64(value), 2).PadLeft(16, '0'); } public static int CountIndices(this string s, char c) { var count = 0; foreach (var _c in s) { if (c == _c) { count++; } } return count; } public static bool IsGuid(string value) { return guidRegex.IsMatch(value); } private static readonly Regex guidRegex = new Regex(@"[a-fA-F0-9]{8}(\-[a-fA-F0-9]{4}){3}\-[a-fA-F0-9]{12}"); public static string PathEllipsis(string s, int maxLength) { var ellipsis = "..."; if (s.Length < maxLength) { return s; } var fileName = Path.GetFileName(s); var directory = Path.GetDirectoryName(s); var maxDirectoryLength = maxLength - fileName.Length - ellipsis.Length; if (maxDirectoryLength > 0) { return directory.Substring(0, maxDirectoryLength) + ellipsis + Path.DirectorySeparatorChar + fileName; } else { return ellipsis + Path.DirectorySeparatorChar + fileName; } } public static string ToHexString(this byte[] bytes) { return BitConverter.ToString(bytes).Replace("-", ""); } } }