StringExtension.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. namespace EasyDevCore.Common
  7. {
  8. /// <summary>
  9. /// String extension helper
  10. /// </summary>
  11. public static class StringExtensions
  12. {
  13. /// <summary>
  14. /// Ins the specified string comparison.
  15. /// </summary>
  16. /// <param name="input">The input.</param>
  17. /// <param name="comparisionType">One of the enumeration values that specifies how the strings will be compared.</param>
  18. /// <param name="args">The arguments.</param>
  19. /// <returns></returns>
  20. public static bool In(this string input, StringComparison comparisionType, params string[] args)
  21. {
  22. if (input == null) return false;
  23. foreach (string value in args)
  24. {
  25. if (!input.Equals(value))
  26. continue;
  27. return true;
  28. }
  29. return false;
  30. }
  31. /// <summary>
  32. /// Determines whether the specified arguments has any.
  33. /// </summary>
  34. /// <param name="input">The input.</param>
  35. /// <param name="args">The arguments.</param>
  36. /// <returns>
  37. /// <c>true</c> if the specified arguments has any; otherwise, <c>false</c>.
  38. /// </returns>
  39. public static bool HasAny(this string input, params string[] args)
  40. {
  41. foreach(var p in args)
  42. {
  43. if (input.IndexOf(p) > -1) return true;
  44. }
  45. return false;
  46. }
  47. /// <summary>
  48. /// Determines whether the specified comparison has any.
  49. /// </summary>
  50. /// <param name="input">The input.</param>
  51. /// <param name="comparison">The comparison.</param>
  52. /// <param name="args">The arguments.</param>
  53. /// <returns>
  54. /// <c>true</c> if the specified comparison has any; otherwise, <c>false</c>.
  55. /// </returns>
  56. public static bool HasAny(this string input, StringComparison comparison, params string[] args)
  57. {
  58. foreach (var p in args)
  59. {
  60. if (input.IndexOf(p, comparison) > -1) return true;
  61. }
  62. return false;
  63. }
  64. /// <summary>
  65. /// Determines whether [is unicode string] [the specified input].
  66. /// </summary>
  67. /// <param name="input">The input.</param>
  68. /// <returns>
  69. /// <c>true</c> if [is unicode string] [the specified input]; otherwise, <c>false</c>.
  70. /// </returns>
  71. public static bool IsUnicode(this string input)
  72. {
  73. return input.ToCharArray().Any(c => c > 255);
  74. }
  75. /// <summary>
  76. /// Removes all leading occurrences of whitespace of input string
  77. /// </summary>
  78. /// <param name="input">The input.</param>
  79. /// <returns></returns>
  80. public static string LTrim(this string input)
  81. {
  82. return input.TrimStart(new char[] { ' ' });
  83. }
  84. /// <summary>
  85. /// Removes all occurrences of whitespace of the end of the input string
  86. /// </summary>
  87. /// <param name="input">The input.</param>
  88. /// <returns></returns>
  89. public static string RTrim(this string input)
  90. {
  91. return input.TrimEnd(new char[] { ' ' });
  92. }
  93. /// <summary>
  94. /// Lefts the specified length.
  95. /// </summary>
  96. /// <param name="input">The input.</param>
  97. /// <param name="len">The length.</param>
  98. /// <returns></returns>
  99. public static string Left(this string input, int len) => (input == null || input.Length <= len) ? input : input.Substring(0, len);
  100. /// <summary>
  101. /// Rights the specified length.
  102. /// </summary>
  103. /// <param name="input">The input.</param>
  104. /// <param name="len">The length.</param>
  105. /// <returns></returns>
  106. public static string Right(this string input, int len) => (input == null || input.Length <= len) ? input : input.Substring(input.Length - len, len);
  107. /// <summary>
  108. /// Replaces with case style.
  109. /// </summary>
  110. /// <param name="text">The text.</param>
  111. /// <param name="oldValue">The old value.</param>
  112. /// <param name="newValue">The new value.</param>
  113. /// <param name="maxMatchLength">Maximum length of the match.</param>
  114. /// <returns></returns>
  115. public static string ReplaceCaseStyle(this string text, string oldValue, string newValue, int maxMatchLength = 0)
  116. {
  117. var caseReplacement = TransferCaseStyle(oldValue, newValue);
  118. return text.Replace(oldValue, caseReplacement);
  119. }
  120. /// <summary>
  121. /// Transfers the case style.
  122. /// </summary>
  123. /// <param name="match">The match.</param>
  124. /// <param name="replacement">The replacement.</param>
  125. /// <param name="maxMatchLength">Maximum length of the match.</param>
  126. /// <returns></returns>
  127. public static string TransferCaseStyle(string match, string replacement, int maxMatchLength = 0)
  128. {
  129. char[] replacementChars = replacement.ToCharArray();
  130. if(maxMatchLength == 0)
  131. {
  132. maxMatchLength = Math.Min(match.Length, replacement.Length);
  133. }
  134. for (int i = 0; i < maxMatchLength; i++)
  135. {
  136. if (char.IsLower(match[i]))
  137. replacementChars[i] = char.ToLower(replacementChars[i]);
  138. else if (char.IsUpper(match[i]))
  139. replacementChars[i] = char.ToUpper(replacementChars[i]);
  140. }
  141. return new string(replacementChars);
  142. }
  143. /// <summary>
  144. /// Regexes the get match values.
  145. /// </summary>
  146. /// <param name="input">The input.</param>
  147. /// <param name="patterns">The patterns.</param>
  148. /// <returns>(Key = Match Value, Value = Group Name)</returns>
  149. public static Dictionary<string, string> RegexGetMatchValues(this string input, string patterns)
  150. {
  151. Dictionary<string, string> matchValues = new();
  152. Regex re = new Regex(patterns);
  153. var groupNames = re.GetGroupNames().Where(x => !(x[0] >= (byte)'0' && x[0] <= (byte)'9')).ToArray();
  154. var matches = re.Matches(input);
  155. foreach (Match m in matches.AsParallel())
  156. {
  157. foreach (var gn in groupNames)
  158. {
  159. var mg = m.Groups[gn];
  160. if (mg.Success)
  161. {
  162. var value = mg.Value;
  163. if (!matchValues.ContainsKey(value))
  164. {
  165. matchValues.Add(value, gn);
  166. }
  167. }
  168. }
  169. }
  170. return matchValues;
  171. }
  172. /// <summary>
  173. /// Regexes the group match.
  174. /// </summary>
  175. /// <param name="input">The input.</param>
  176. /// <param name="patterns">The patterns.</param>
  177. /// <param name="matchHandler">The match handler is Func(Group match, string groupname, string replactedText).</param>
  178. public static void RegexGroupMatch(this string input, string patterns, Action<Group> matchHandler)
  179. {
  180. Regex re = new Regex(patterns);
  181. var groupNames = re.GetGroupNames().Where(x => !(x[0] >= (byte)'0' && x[0] <= (byte)'9')).ToArray();
  182. var matches = re.Matches(input);
  183. foreach (Match m in matches.AsParallel())
  184. {
  185. foreach (var gn in groupNames)
  186. {
  187. var mg = m.Groups[gn];
  188. if (mg.Success)
  189. {
  190. matchHandler(mg);
  191. }
  192. }
  193. }
  194. }
  195. /// <summary>
  196. /// Regexes the group replace.
  197. /// </summary>
  198. /// <param name="input">The input.</param>
  199. /// <param name="patterns">The patterns.</param>
  200. /// <param name="matchHandler">The match handler is Func(Group match, string groupname, string replactedText).</param>
  201. /// <returns></returns>
  202. public static string RegexGroupReplace(this string input, string patterns, Func<Group, string, string> matchHandler)
  203. {
  204. Regex re = new Regex(patterns);
  205. var groupNames = re.GetGroupNames().Where(x => !(x[0] >= (byte)'0' && x[0] <= (byte)'9')).ToArray();
  206. return re.Replace(input, (MatchEvaluator)((m) =>
  207. {
  208. foreach (var gn in groupNames)
  209. {
  210. var mg = m.Groups[gn];
  211. if (mg.Success)
  212. {
  213. return matchHandler(mg, gn);
  214. }
  215. }
  216. return m.Value;
  217. }));
  218. }
  219. /// <summary>
  220. /// Substitudes the specified input.
  221. /// </summary>
  222. /// <param name="input">The input.</param>
  223. /// <param name="index">The index.</param>
  224. /// <param name="length">The length.</param>
  225. /// <param name="replacedText">The replaced text.</param>
  226. /// <returns></returns>
  227. public static string Substitude(this string input, int index, int length, string replacedText)
  228. {
  229. return input.Substring(0, index) + replacedText + (index + length > input.Length ? "" : input.Substring(index + length));
  230. }
  231. /// <summary>
  232. /// Substitudes the specified input.
  233. /// </summary>
  234. /// <param name="input">The input.</param>
  235. /// <param name="index">The index.</param>
  236. /// <param name="replacedText">The replaced text.</param>
  237. /// <returns></returns>
  238. public static string Substitude(this string input, int index, string replacedText)
  239. {
  240. return input.Substring(0, index) + replacedText + (index + replacedText.Length > input.Length ? "" : input.Substring(index + replacedText.Length));
  241. }
  242. /// <summary>
  243. /// Splits the string.
  244. /// </summary>
  245. /// <param name="input">The input.</param>
  246. /// <param name="seperator">The seperator.</param>
  247. /// <param name="splitOptions">The split options.</param>
  248. /// <param name="trimSpace">if set to <c>true</c> [trim space].</param>
  249. /// <returns></returns>
  250. public static string[] SplitString(this string input, string seperator, StringSplitOptions splitOptions = StringSplitOptions.RemoveEmptyEntries, bool trimSpace = false)
  251. {
  252. string[] result = input.Split(new string[] { seperator }, splitOptions);
  253. if (trimSpace)
  254. {
  255. return result.Select(s => s.Trim()).ToArray();
  256. }
  257. return result;
  258. }
  259. /// <summary>
  260. /// Splits the by comma or semi colon (,;).
  261. /// </summary>
  262. /// <param name="input">The input.</param>
  263. /// <param name="splitOptions">The split options.</param>
  264. /// <param name="trimSpace">if set to <c>true</c> [trim space].</param>
  265. /// <returns>System.String[].</returns>
  266. public static string[] SplitByCommaOrSemiColon(this string input, StringSplitOptions splitOptions = StringSplitOptions.RemoveEmptyEntries, bool trimSpace = false)
  267. {
  268. string[] result = input.Split( new char[] { ',', ';' }, splitOptions);
  269. if (trimSpace)
  270. {
  271. return result.Select(s => s.Trim()).ToArray();
  272. }
  273. return result;
  274. }
  275. /// <summary>
  276. /// Splits the comma string.
  277. /// </summary>
  278. /// <param name="input">The input.</param>
  279. /// <param name="splitOptions">The split options.</param>
  280. /// <param name="trimSpace">if set to <c>true</c> [trim space].</param>
  281. /// <returns></returns>
  282. public static string[] SplitCommaString(this string input, StringSplitOptions splitOptions = StringSplitOptions.RemoveEmptyEntries, bool trimSpace = false)
  283. {
  284. string[] result = input.Split(new char[] { ',' }, splitOptions);
  285. if (trimSpace)
  286. {
  287. return result.Select(s => s.Trim()).ToArray();
  288. }
  289. return result;
  290. }
  291. /// <summary>
  292. /// Splits the semi colon string.
  293. /// </summary>
  294. /// <param name="input">The input.</param>
  295. /// <param name="splitOptions">The split options.</param>
  296. /// <param name="trimSpace">if set to <c>true</c> [trim space].</param>
  297. /// <returns></returns>
  298. public static string[] SplitSemiColonString(this string input, StringSplitOptions splitOptions = StringSplitOptions.RemoveEmptyEntries, bool trimSpace = false)
  299. {
  300. string[] result = input.Split(new char[] { ';' }, splitOptions);
  301. if (trimSpace)
  302. {
  303. return result.Select(s => s.Trim()).ToArray();
  304. }
  305. return result;
  306. }
  307. /// <summary>
  308. /// A case insenstive replace function.
  309. /// </summary>
  310. /// <param name="value">The string to examine.</param>
  311. /// <param name="newValue">The value to replace.</param>
  312. /// <param name="oldValue">The new value to be inserted</param>
  313. /// <returns>A string</returns>
  314. public static string CaseInsenstiveReplace(this string value, string newValue, string oldValue)
  315. {
  316. Regex regEx = new Regex(oldValue, RegexOptions.IgnoreCase | RegexOptions.Multiline);
  317. return regEx.Replace(value, newValue);
  318. }
  319. /// <summary>
  320. /// Removes the new line (\n) and carriage return (\r) symbols.
  321. /// </summary>
  322. /// <param name="value">The string to search.</param>
  323. /// <param name="addSpace">If true, adds a space (" ") for each newline and carriage
  324. /// return found.</param>
  325. /// <returns>A string</returns>
  326. public static string RemoveNewLines(this string value, bool addSpace = false)
  327. {
  328. string replace = string.Empty;
  329. if (addSpace)
  330. replace = " ";
  331. string pattern = @"[\r|\n]";
  332. Regex regEx = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
  333. return regEx.Replace(value, replace);
  334. }
  335. /// <summary>
  336. /// Reverse a string.
  337. /// </summary>
  338. /// <param name="value">The string to reverse</param>
  339. /// <returns>A string</returns>
  340. public static string Reverse(this string value)
  341. {
  342. if (value.Length <= 1)
  343. return value;
  344. char[] c = value.ToCharArray();
  345. StringBuilder sb = new StringBuilder(c.Length);
  346. for (int i = c.Length - 1; i > -1; i--)
  347. sb.Append(c[i]);
  348. return sb.ToString();
  349. }
  350. /// <summary>
  351. /// Converts a string to sentence case.
  352. /// </summary>
  353. /// <param name="value">The string to convert.</param>
  354. /// <returns>A string</returns>
  355. public static string SentenceCase(this string value)
  356. {
  357. if (value.Length < 1)
  358. return value;
  359. // start by converting entire string to lower case
  360. var lowerCase = value.ToLower();
  361. // matches the first sentence of a string, as well as subsequent sentences
  362. var r = new Regex(@"(^[a-z])|\.\s+(.)", RegexOptions.ExplicitCapture);
  363. // MatchEvaluator delegate defines replacement of setence starts to uppercase
  364. return r.Replace(lowerCase, s => s.Value.ToUpper());
  365. }
  366. /// <summary>
  367. /// Converts a string to title case.
  368. /// </summary>
  369. /// <param name="value">The string to convert.</param>
  370. /// <returns>A string.</returns>
  371. public static string TitleCase(this string value)
  372. {
  373. if (value.Length < 1)
  374. return value;
  375. // start by converting entire string to lower case
  376. var lowerCase = value.ToLower();
  377. // matches the first sentence of a string, as well as subsequent sentences
  378. var r = new Regex(@"\b(\w)", RegexOptions.ExplicitCapture);
  379. // MatchEvaluator delegate defines replacement of setence starts to uppercase
  380. return r.Replace(lowerCase, s => s.Value.ToUpper());
  381. }
  382. /// <summary>
  383. /// Determines whether the specified input is match.
  384. /// </summary>
  385. /// <param name="input">The input.</param>
  386. /// <param name="pattern">The pattern (regular expression).</param>
  387. /// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
  388. /// <returns>
  389. /// <c>true</c> if the specified input is match; otherwise, <c>false</c>.
  390. /// </returns>
  391. public static bool IsMatch(this string input, string pattern, bool ignoreCase = false)
  392. {
  393. return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase);
  394. }
  395. /// <summary>
  396. /// string like operator.
  397. /// </summary>
  398. /// <param name="input">The input.</param>
  399. /// <param name="pattern">The pattern.</param>
  400. /// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
  401. /// <returns></returns>
  402. public static bool StringLike(this string input, string pattern, bool ignoreCase = false)
  403. {
  404. return SQLLike(input, pattern.Replace("?", "_").Replace("*", "%"), ignoreCase);
  405. }
  406. /// <summary>
  407. /// SQL like operator.
  408. /// </summary>
  409. /// <param name="input">The input.</param>
  410. /// <param name="pattern">The pattern.</param>
  411. /// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
  412. /// <returns></returns>
  413. public static bool SQLLike(this string input, string pattern, bool ignoreCase)
  414. {
  415. /* Turn "off" all regular expression related syntax in
  416. * the pattern string. */
  417. pattern = Regex.Escape(pattern);
  418. /* Replace the LIKE wildcard metacharacters with the
  419. * equivalent regular expression metacharacters. */
  420. pattern = pattern.Replace("_", ".").Replace("%", ".*");
  421. /* The previous call to Regex.Escape actually turned off
  422. * too many metacharacters, i.e. those which are recognized by
  423. * both the regular expression engine and the LIKE
  424. * statement ([...] and [^...]). Those metacharacters have
  425. * to be manually unescaped here. */
  426. pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^");
  427. if (ignoreCase)
  428. {
  429. return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase);
  430. }
  431. else
  432. {
  433. return Regex.IsMatch(input, pattern);
  434. }
  435. }
  436. /// <summary>
  437. /// SQLs the like pattern.
  438. /// </summary>
  439. /// <param name="input">The input.</param>
  440. /// <param name="pattern">The pattern.</param>
  441. /// <returns></returns>
  442. public static bool SQLLike(this string input, string pattern)
  443. {
  444. bool isMatch = true,
  445. isWildCardOn = false,
  446. isCharWildCardOn = false,
  447. isCharSetOn = false,
  448. isNotCharSetOn = false,
  449. endOfPattern = false;
  450. int lastWildCard = -1;
  451. int patternIndex = 0;
  452. List<char> set = new List<char>();
  453. char p = '\0';
  454. for (int i = 0; i < input.Length; i++)
  455. {
  456. char c = input[i];
  457. endOfPattern = (patternIndex >= pattern.Length);
  458. if (!endOfPattern)
  459. {
  460. p = pattern[patternIndex];
  461. if (!isWildCardOn && p == '%')
  462. {
  463. lastWildCard = patternIndex;
  464. isWildCardOn = true;
  465. while (patternIndex < pattern.Length &&
  466. pattern[patternIndex] == '%')
  467. {
  468. patternIndex++;
  469. }
  470. if (patternIndex >= pattern.Length) p = '\0';
  471. else p = pattern[patternIndex];
  472. }
  473. else if (p == '_')
  474. {
  475. isCharWildCardOn = true;
  476. patternIndex++;
  477. }
  478. else if (p == '[')
  479. {
  480. if (pattern[++patternIndex] == '^')
  481. {
  482. isNotCharSetOn = true;
  483. patternIndex++;
  484. }
  485. else isCharSetOn = true;
  486. set.Clear();
  487. if (pattern[patternIndex + 1] == '-' && pattern[patternIndex + 3] == ']')
  488. {
  489. char start = char.ToUpper(pattern[patternIndex]);
  490. patternIndex += 2;
  491. char end = char.ToUpper(pattern[patternIndex]);
  492. if (start <= end)
  493. {
  494. for (char ci = start; ci <= end; ci++)
  495. {
  496. set.Add(ci);
  497. }
  498. }
  499. patternIndex++;
  500. }
  501. while (patternIndex < pattern.Length &&
  502. pattern[patternIndex] != ']')
  503. {
  504. set.Add(pattern[patternIndex]);
  505. patternIndex++;
  506. }
  507. patternIndex++;
  508. }
  509. }
  510. if (isWildCardOn)
  511. {
  512. if (char.ToUpper(c) == char.ToUpper(p))
  513. {
  514. isWildCardOn = false;
  515. patternIndex++;
  516. }
  517. }
  518. else if (isCharWildCardOn)
  519. {
  520. isCharWildCardOn = false;
  521. }
  522. else if (isCharSetOn || isNotCharSetOn)
  523. {
  524. bool charMatch = (set.Contains(char.ToUpper(c)));
  525. if ((isNotCharSetOn && charMatch) || (isCharSetOn && !charMatch))
  526. {
  527. if (lastWildCard >= 0) patternIndex = lastWildCard;
  528. else
  529. {
  530. isMatch = false;
  531. break;
  532. }
  533. }
  534. isNotCharSetOn = isCharSetOn = false;
  535. }
  536. else
  537. {
  538. if (char.ToUpper(c) == char.ToUpper(p))
  539. {
  540. patternIndex++;
  541. }
  542. else
  543. {
  544. if (lastWildCard >= 0) patternIndex = lastWildCard;
  545. else
  546. {
  547. isMatch = false;
  548. break;
  549. }
  550. }
  551. }
  552. }
  553. endOfPattern = (patternIndex >= pattern.Length);
  554. if (isMatch && !endOfPattern)
  555. {
  556. bool isOnlyWildCards = true;
  557. for (int i = patternIndex; i < pattern.Length; i++)
  558. {
  559. if (pattern[i] != '%')
  560. {
  561. isOnlyWildCards = false;
  562. break;
  563. }
  564. }
  565. if (isOnlyWildCards) endOfPattern = true;
  566. }
  567. return isMatch && endOfPattern;
  568. }
  569. }
  570. }