A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections; 3using System.Collections.Generic; 4using System.IO; 5using System.Linq; 6using System.Text; 7using System.Text.RegularExpressions; 8 9namespace Unity.VisualScripting 10{ 11 public static class StringUtility 12 { 13 public static bool IsNullOrWhiteSpace(string s) 14 { 15 return s == null || s.Trim() == string.Empty; 16 } 17 18 public static string FallbackEmpty(string s, string fallback) 19 { 20 if (string.IsNullOrEmpty(s)) 21 { 22 s = fallback; 23 } 24 25 return s; 26 } 27 28 public static string FallbackWhitespace(string s, string fallback) 29 { 30 if (IsNullOrWhiteSpace(s)) 31 { 32 s = fallback; 33 } 34 35 return s; 36 } 37 38 public static void AppendLineFormat(this StringBuilder sb, string format, params object[] args) 39 { 40 sb.AppendFormat(format, args); 41 sb.AppendLine(); 42 } 43 44 public static string ToSeparatedString(this IEnumerable enumerable, string separator) 45 { 46 return string.Join(separator, enumerable.Cast<object>().Select(o => o?.ToString() ?? "(null)").ToArray()); 47 } 48 49 public static string ToCommaSeparatedString(this IEnumerable enumerable) 50 { 51 return ToSeparatedString(enumerable, ", "); 52 } 53 54 public static string ToLineSeparatedString(this IEnumerable enumerable) 55 { 56 return ToSeparatedString(enumerable, Environment.NewLine); 57 } 58 59 public static bool ContainsInsensitive(this string haystack, string needle) 60 { 61 return haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0; 62 } 63 64 public static IEnumerable<int> AllIndexesOf(this string haystack, string needle) 65 { 66 if (string.IsNullOrEmpty(needle)) 67 { 68 yield break; 69 } 70 71 for (var index = 0; ; index += needle.Length) 72 { 73 index = haystack.IndexOf(needle, index, StringComparison.OrdinalIgnoreCase); 74 75 if (index == -1) 76 { 77 break; 78 } 79 80 yield return index; 81 } 82 } 83 84 public static string Filter(this string s, bool letters = true, bool numbers = true, bool whitespace = true, bool symbols = true, bool punctuation = true) 85 { 86 var sb = new StringBuilder(); 87 88 foreach (var c in s) 89 { 90 if ((!letters && char.IsLetter(c)) || 91 (!numbers && char.IsNumber(c)) || 92 (!whitespace && char.IsWhiteSpace(c)) || 93 (!symbols && char.IsSymbol(c)) || 94 (!punctuation && char.IsPunctuation(c))) 95 { 96 continue; 97 } 98 99 sb.Append(c); 100 } 101 102 return sb.ToString(); 103 } 104 105 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) 106 { 107 var sb = new StringBuilder(); 108 109 var wasFiltered = false; 110 111 foreach (var c in s) 112 { 113 if ((!letters && char.IsLetter(c)) || 114 (!numbers && char.IsNumber(c)) || 115 (!whitespace && char.IsWhiteSpace(c)) || 116 (!symbols && char.IsSymbol(c)) || 117 (!punctuation && char.IsPunctuation(c))) 118 { 119 if (!merge || !wasFiltered) 120 { 121 sb.Append(replacement); 122 } 123 124 wasFiltered = true; 125 } 126 else 127 { 128 sb.Append(c); 129 130 wasFiltered = false; 131 } 132 } 133 134 return sb.ToString(); 135 } 136 137 public static string Prettify(this string s) 138 { 139 return s.FirstCharacterToUpper().SplitWords(' '); 140 } 141 142 public static bool IsWordDelimiter(char c) 143 { 144 return char.IsWhiteSpace(c) || char.IsSymbol(c) || char.IsPunctuation(c); 145 } 146 147 public static bool IsWordBeginning(char? previous, char current, char? next) 148 { 149 var isFirst = previous == null; 150 var isLast = next == null; 151 152 var isLetter = char.IsLetter(current); 153 var wasLetter = previous != null && char.IsLetter(previous.Value); 154 155 var isNumber = char.IsNumber(current); 156 var wasNumber = previous != null && char.IsNumber(previous.Value); 157 158 var isUpper = char.IsUpper(current); 159 var wasUpper = previous != null && char.IsUpper(previous.Value); 160 161 var isDelimiter = IsWordDelimiter(current); 162 var wasDelimiter = previous != null && IsWordDelimiter(previous.Value); 163 164 var willBeLower = next != null && char.IsLower(next.Value); 165 166 return 167 (!isDelimiter && isFirst) || 168 (!isDelimiter && wasDelimiter) || 169 (isLetter && wasLetter && isUpper && !wasUpper) || // camelCase => camel_Case 170 (isLetter && wasLetter && isUpper && wasUpper && !isLast && willBeLower) || // => ABBRWord => ABBR_Word 171 (isNumber && wasLetter) || // Vector3 => Vector_3 172 (isLetter && wasNumber && isUpper && willBeLower); // Word1Word => Word_1_Word, Word1word => Word_1word 173 } 174 175 public static bool IsWordBeginning(string s, int index) 176 { 177 Ensure.That(nameof(index)).IsGte(index, 0); 178 Ensure.That(nameof(index)).IsLt(index, s.Length); 179 180 var previous = index > 0 ? s[index - 1] : (char?)null; 181 var current = s[index]; 182 var next = index < s.Length - 1 ? s[index + 1] : (char?)null; 183 184 return IsWordBeginning(previous, current, next); 185 } 186 187 public static string SplitWords(this string s, char separator) 188 { 189 var sb = new StringBuilder(); 190 191 for (var i = 0; i < s.Length; i++) 192 { 193 var c = s[i]; 194 195 if (i > 0 && IsWordBeginning(s, i)) 196 { 197 sb.Append(separator); 198 } 199 200 sb.Append(c); 201 } 202 203 return sb.ToString(); 204 } 205 206 public static string RemoveConsecutiveCharacters(this string s, char c) 207 { 208 var sb = new StringBuilder(); 209 210 var previous = '\0'; 211 212 foreach (var current in s) 213 { 214 if (current != c || current != previous) 215 { 216 sb.Append(current); 217 previous = current; 218 } 219 } 220 221 return sb.ToString(); 222 } 223 224 public static string ReplaceMultiple(this string s, HashSet<char> haystacks, char replacement) 225 { 226 Ensure.That(nameof(haystacks)).IsNotNull(haystacks); 227 228 var sb = new StringBuilder(); 229 230 foreach (var current in s) 231 { 232 if (haystacks.Contains(current)) 233 { 234 sb.Append(replacement); 235 } 236 else 237 { 238 sb.Append(current); 239 } 240 } 241 242 return sb.ToString(); 243 } 244 245 public static string Truncate(this string value, int maxLength, string suffix = "...") 246 { 247 return value.Length <= maxLength ? value : value.Substring(0, maxLength) + suffix; 248 } 249 250 public static string TrimEnd(this string source, string value) 251 { 252 if (!source.EndsWith(value)) 253 { 254 return source; 255 } 256 257 return source.Remove(source.LastIndexOf(value)); 258 } 259 260 public static string TrimStart(this string source, string value) 261 { 262 if (!source.StartsWith(value)) 263 { 264 return source; 265 } 266 267 return source.Substring(value.Length); 268 } 269 270 public static string FirstCharacterToLower(this string s) 271 { 272 if (string.IsNullOrEmpty(s) || char.IsLower(s, 0)) 273 { 274 return s; 275 } 276 277 return char.ToLowerInvariant(s[0]) + s.Substring(1); 278 } 279 280 public static string FirstCharacterToUpper(this string s) 281 { 282 if (string.IsNullOrEmpty(s) || char.IsUpper(s, 0)) 283 { 284 return s; 285 } 286 287 return char.ToUpperInvariant(s[0]) + s.Substring(1); 288 } 289 290 public static string PartBefore(this string s, char c) 291 { 292 Ensure.That(nameof(s)).IsNotNull(s); 293 294 var index = s.IndexOf(c); 295 296 if (index > 0) 297 { 298 return s.Substring(0, index); 299 } 300 else 301 { 302 return s; 303 } 304 } 305 306 public static string PartAfter(this string s, char c) 307 { 308 Ensure.That(nameof(s)).IsNotNull(s); 309 310 var index = s.IndexOf(c); 311 312 if (index > 0) 313 { 314 return s.Substring(index + 1); 315 } 316 else 317 { 318 return s; 319 } 320 } 321 322 public static void PartsAround(this string s, char c, out string before, out string after) 323 { 324 Ensure.That(nameof(s)).IsNotNull(s); 325 326 var index = s.IndexOf(c); 327 328 if (index > 0) 329 { 330 before = s.Substring(0, index); 331 after = s.Substring(index + 1); 332 } 333 else 334 { 335 before = s; 336 after = null; 337 } 338 } 339 340 // Faster equivalents for chars 341 342 public static bool EndsWith(this string s, char c) 343 { 344 Ensure.That(nameof(s)).IsNotNull(s); 345 346 return s[s.Length - 1] == c; 347 } 348 349 public static bool StartsWith(this string s, char c) 350 { 351 Ensure.That(nameof(s)).IsNotNull(s); 352 353 return s[0] == c; 354 } 355 356 public static bool Contains(this string s, char c) 357 { 358 Ensure.That(nameof(s)).IsNotNull(s); 359 360 for (int i = 0; i < s.Length; i++) 361 { 362 if (s[i] == c) 363 { 364 return true; 365 } 366 } 367 368 return false; 369 } 370 371 public static string NullIfEmpty(this string s) 372 { 373 if (s == string.Empty) 374 { 375 return null; 376 } 377 else 378 { 379 return s; 380 } 381 } 382 383 public static string ToBinaryString(this int value) 384 { 385 return Convert.ToString(value, 2).PadLeft(8, '0'); 386 } 387 388 public static string ToBinaryString(this long value) 389 { 390 return Convert.ToString(value, 2).PadLeft(16, '0'); 391 } 392 393 public static string ToBinaryString(this Enum value) 394 { 395 return Convert.ToString(Convert.ToInt64(value), 2).PadLeft(16, '0'); 396 } 397 398 public static int CountIndices(this string s, char c) 399 { 400 var count = 0; 401 402 foreach (var _c in s) 403 { 404 if (c == _c) 405 { 406 count++; 407 } 408 } 409 410 return count; 411 } 412 413 public static bool IsGuid(string value) 414 { 415 return guidRegex.IsMatch(value); 416 } 417 418 private static readonly Regex guidRegex = new Regex(@"[a-fA-F0-9]{8}(\-[a-fA-F0-9]{4}){3}\-[a-fA-F0-9]{12}"); 419 420 public static string PathEllipsis(string s, int maxLength) 421 { 422 var ellipsis = "..."; 423 424 if (s.Length < maxLength) 425 { 426 return s; 427 } 428 429 var fileName = Path.GetFileName(s); 430 var directory = Path.GetDirectoryName(s); 431 432 var maxDirectoryLength = maxLength - fileName.Length - ellipsis.Length; 433 434 if (maxDirectoryLength > 0) 435 { 436 return directory.Substring(0, maxDirectoryLength) + ellipsis + Path.DirectorySeparatorChar + fileName; 437 } 438 else 439 { 440 return ellipsis + Path.DirectorySeparatorChar + fileName; 441 } 442 } 443 444 public static string ToHexString(this byte[] bytes) 445 { 446 return BitConverter.ToString(bytes).Replace("-", ""); 447 } 448 } 449}