StringExt.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. namespace Quadarax.Foundation.Core.Value.Extensions
  7. {
  8. public static class StringExt
  9. {
  10. /// <summary>
  11. /// Default string comparer for like operator.
  12. /// </summary>
  13. public static IEqualityComparer<string> DefaultLikeComparer { get; set; } = new StringPatternComparer();
  14. public static string Align(this string value, int size)
  15. {
  16. if (string.IsNullOrEmpty(value)) return new string(' ', size);
  17. if (value.Length < size)
  18. return value + new string(' ', size - value.Length);
  19. return Truncate(value, size);
  20. }
  21. public static string Truncate(this string value, int maxLength)
  22. {
  23. if (string.IsNullOrEmpty(value)) return value;
  24. return value.Length <= maxLength ? value : value.Substring(0, maxLength);
  25. }
  26. public static int SeparateValueCount(this string value, string separator)
  27. {
  28. var nSepLen = separator.Length;
  29. var nStrLen = value.Length;
  30. if (nStrLen == 0 || nSepLen == 0)
  31. return nSepLen == 0 && nStrLen > 0 ? 1 : 0;
  32. var nCnt = 0;
  33. var nPos = value.IndexOf(separator, StringComparison.Ordinal);
  34. while (nPos >= 0)
  35. {
  36. nCnt++;
  37. nPos = value.IndexOf(separator, nPos + nSepLen, StringComparison.Ordinal);
  38. }
  39. return nCnt + 1;
  40. }
  41. public static string SeparateValue(this string value, string separator, int zeroBasedIndex)
  42. {
  43. var nSepLen = separator.Length;
  44. var nStrLen = value.Length;
  45. if (nStrLen == 0 || nSepLen == 0 || nSepLen > nStrLen)
  46. throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
  47. var nLastPos = 0;
  48. var nPos = value.IndexOf(separator, StringComparison.Ordinal);
  49. while (nPos >= 0)
  50. {
  51. if (zeroBasedIndex == 0)
  52. break;
  53. nLastPos = nPos + nSepLen;
  54. nPos = value.IndexOf(separator, nPos + nSepLen, StringComparison.Ordinal);
  55. zeroBasedIndex--;
  56. }
  57. if (zeroBasedIndex > 0)
  58. throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
  59. return value.Substring(nLastPos, nPos == -1 ? nStrLen - nLastPos : nPos - nLastPos);
  60. }
  61. public static string SeparateValueLast(this string value, string separator)
  62. {
  63. var nSepLen = separator.Length;
  64. var nStrLen = value.Length;
  65. if (nStrLen == 0 || nSepLen == 0 || nSepLen > nStrLen)
  66. throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
  67. return value.SeparateValue(separator, value.SeparateValueCount(separator) - 1);
  68. }
  69. public static string RemoveLast(this string value)
  70. {
  71. return value.Length == 0 ? string.Empty : value.Substring(0, value.Length - 1);
  72. }
  73. public static string RemoveFirst(this string value)
  74. {
  75. return value.Length == 0 ? string.Empty : value.Substring(1, value.Length - 1);
  76. }
  77. public static string RemoveDecorator(this string value, string decoratorLeft, string? decoratorRight = null)
  78. {
  79. if (string.IsNullOrEmpty(value) || string.IsNullOrEmpty(decoratorLeft))
  80. return value;
  81. if (decoratorRight == null)
  82. decoratorRight = decoratorLeft;
  83. if (value.StartsWith(decoratorLeft, StringComparison.InvariantCulture) && value.EndsWith(decoratorRight, StringComparison.InvariantCulture))
  84. return value.Substring(decoratorLeft.Length, value.Length - decoratorLeft.Length - decoratorRight.Length);
  85. return value;
  86. }
  87. public static int MaxLineChars(this string value)
  88. {
  89. var aParts = value.Split("/n");
  90. return aParts.Length > 0 ? aParts.Max(x => x.Length) : value.Length;
  91. }
  92. public static string[] Split(this string value, string separator)
  93. {
  94. return value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries);
  95. }
  96. public static Tuple<string, string>[] Extract(this string value, params string[] hashes) //hashtype, tashname
  97. {
  98. var oResult = new List<Tuple<string, string>>();
  99. if (hashes.Length == 0)
  100. return new Tuple<string, string>[0];
  101. var nLenIndex = value.Length + 1;
  102. foreach (var sHash in hashes)
  103. {
  104. var bBeginBlock = true;
  105. var nPos = value.IndexOf(sHash, StringComparison.InvariantCulture);
  106. var nLenHash = sHash.Length;
  107. if (nPos < 0)
  108. continue;
  109. while (nPos >= 0)
  110. {
  111. if (nPos + nLenHash > nLenIndex)
  112. break;
  113. var nEndPos = value.IndexOf(sHash, nPos + nLenHash, StringComparison.InvariantCulture);
  114. if (nEndPos < 0)
  115. break;
  116. if (bBeginBlock)
  117. {
  118. var oEntry = new Tuple<string, string>(sHash, value.Substring(nPos + nLenHash, nEndPos - nPos - nLenHash));
  119. if (!oResult.Contains(oEntry))
  120. oResult.Add(oEntry);
  121. }
  122. nPos = nEndPos;
  123. bBeginBlock = !bBeginBlock;
  124. }
  125. }
  126. return oResult.ToArray();
  127. }
  128. public static string EnsurePathBackslash(this string value)
  129. {
  130. if (string.IsNullOrEmpty(value))
  131. return value;
  132. value = value.Trim();
  133. if (value.EndsWith(Path.DirectorySeparatorChar))
  134. return value;
  135. return value + Path.DirectorySeparatorChar;
  136. }
  137. public static string EnsurePathNonBackslash(this string value)
  138. {
  139. if (string.IsNullOrEmpty(value))
  140. return value;
  141. value = value.Trim();
  142. if (!value.EndsWith(Path.DirectorySeparatorChar))
  143. return value;
  144. return value.RemoveLast();
  145. }
  146. public static string RemoveWhitespace(this string input)
  147. {
  148. return new string(input.ToCharArray()
  149. .Where(c => !Char.IsWhiteSpace(c))
  150. .ToArray());
  151. }
  152. /// <summary>
  153. /// Performs like operator on input string. customComparer can be provides to override default comparer.
  154. /// </summary>
  155. /// <param name="input">Input string to compare</param>
  156. /// <param name="searchPattern">Search pattern corresponds with comparer implementation.</param>
  157. /// <param name="customComparer">Custom comparer. If not set, use default comparer (defined in <see cref="DefaultLikeComparer"/>)</param>
  158. /// <returns></returns>
  159. public static bool Like(this string input, string searchPattern, IEqualityComparer<string>? customComparer = null)
  160. {
  161. customComparer ??= DefaultLikeComparer;
  162. return customComparer.Equals(input, searchPattern);
  163. }
  164. /// <summary>
  165. /// Splits a string by the specified separator, but ignores separators that are within brackets/quotes.
  166. /// </summary>
  167. /// <param name="value">The string to split</param>
  168. /// <param name="separator">The separator string to split on</param>
  169. /// <param name="beginBracket">The opening bracket/quote character(s)</param>
  170. /// <param name="endBracket">The closing bracket/quote character(s). If null, uses the same as beginBracket</param>
  171. /// <param name="removeEmptyItems">If true, removes empty entries from the result</param>
  172. /// <param name="trimValues">If true, trims whitespace from each result value</param>
  173. /// <param name="keepBracket">If true, keeps the begin and end brackets in the result</param>
  174. /// <returns>Array of split string parts</returns>
  175. public static string[] SplitQuoted(this string value, string separator, string beginBracket, string? endBracket = null, bool removeEmptyItems = true, bool trimValues = true, bool keepBracket = false)
  176. {
  177. if (string.IsNullOrEmpty(value))
  178. return new string[] { value ?? string.Empty };
  179. if (string.IsNullOrEmpty(separator))
  180. throw new ArgumentException("Separator cannot be null or empty", nameof(separator));
  181. if (string.IsNullOrEmpty(beginBracket))
  182. throw new ArgumentException("Begin bracket cannot be null or empty", nameof(beginBracket));
  183. // If endBracket is not specified, use the same as beginBracket
  184. endBracket ??= beginBracket;
  185. var result = new List<string>();
  186. var currentPart = new StringBuilder();
  187. var i = 0;
  188. while (i < value.Length)
  189. {
  190. // Check if we're at the start of a separator
  191. if (i <= value.Length - separator.Length &&
  192. value.Substring(i, separator.Length) == separator)
  193. {
  194. // Found separator outside brackets, so add current part to result
  195. result.Add(currentPart.ToString());
  196. currentPart.Clear();
  197. i += separator.Length;
  198. continue;
  199. }
  200. // Check if we're at the start of a begin bracket
  201. if (i <= value.Length - beginBracket.Length &&
  202. value.Substring(i, beginBracket.Length) == beginBracket)
  203. {
  204. // Found begin bracket, add it to current part if keepBracket is true
  205. if (keepBracket)
  206. {
  207. currentPart.Append(beginBracket);
  208. }
  209. i += beginBracket.Length;
  210. // Now find the matching end bracket
  211. var bracketDepth = 1;
  212. while (i < value.Length && bracketDepth > 0)
  213. {
  214. // Check for end brackets first (important for single-character brackets like quotes)
  215. if (i <= value.Length - endBracket.Length &&
  216. value.Substring(i, endBracket.Length) == endBracket)
  217. {
  218. i += endBracket.Length;
  219. bracketDepth--;
  220. // Add the end bracket if we're still nested or if keepBracket is true for outermost
  221. if (bracketDepth > 0 || keepBracket)
  222. {
  223. currentPart.Append(endBracket);
  224. }
  225. }
  226. // Check for nested begin brackets (only if different from end bracket or if we haven't found end bracket)
  227. else if (beginBracket != endBracket &&
  228. i <= value.Length - beginBracket.Length &&
  229. value.Substring(i, beginBracket.Length) == beginBracket)
  230. {
  231. currentPart.Append(beginBracket);
  232. i += beginBracket.Length;
  233. bracketDepth++;
  234. }
  235. else
  236. {
  237. // Regular character inside brackets
  238. currentPart.Append(value[i]);
  239. i++;
  240. }
  241. }
  242. continue;
  243. }
  244. // Regular character outside brackets
  245. currentPart.Append(value[i]);
  246. i++;
  247. }
  248. // Add the last part
  249. result.Add(currentPart.ToString());
  250. // Process the result based on options
  251. var finalResult = new List<string>();
  252. foreach (var part in result)
  253. {
  254. var processedPart = trimValues ? part.Trim() : part;
  255. if (!removeEmptyItems || !string.IsNullOrEmpty(processedPart))
  256. {
  257. finalResult.Add(processedPart);
  258. }
  259. }
  260. return finalResult.ToArray();
  261. }
  262. }
  263. }