|
|
@@ -1,165 +1,278 @@
|
|
|
-using System;
|
|
|
-using System.Collections.Generic;
|
|
|
-using System.IO;
|
|
|
-using System.Linq;
|
|
|
-
|
|
|
-namespace Quadarax.Foundation.Core.Value.Extensions
|
|
|
-{
|
|
|
- public static class StringExt
|
|
|
- {
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
+using System.Linq;
|
|
|
+using System.Text;
|
|
|
+
|
|
|
+namespace Quadarax.Foundation.Core.Value.Extensions
|
|
|
+{
|
|
|
+ public static class StringExt
|
|
|
+ {
|
|
|
/// <summary>
|
|
|
/// Default string comparer for like operator.
|
|
|
- /// </summary>
|
|
|
- public static IEqualityComparer<string> DefaultLikeComparer { get; set; } = new StringPatternComparer();
|
|
|
-
|
|
|
- public static string Align(this string value, int size)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(value)) return new string(' ', size);
|
|
|
- if (value.Length < size)
|
|
|
- return value + new string(' ', size - value.Length);
|
|
|
- return Truncate(value, size);
|
|
|
- }
|
|
|
- public static string Truncate(this string value, int maxLength)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(value)) return value;
|
|
|
- return value.Length <= maxLength ? value : value.Substring(0, maxLength);
|
|
|
- }
|
|
|
-
|
|
|
- public static int SeparateValueCount(this string value, string separator)
|
|
|
- {
|
|
|
- var nSepLen = separator.Length;
|
|
|
- var nStrLen = value.Length;
|
|
|
- if (nStrLen == 0 || nSepLen == 0)
|
|
|
- return nSepLen == 0 && nStrLen > 0 ? 1 : 0;
|
|
|
- var nCnt = 0;
|
|
|
- var nPos = value.IndexOf(separator, StringComparison.Ordinal);
|
|
|
- while (nPos >= 0)
|
|
|
- {
|
|
|
- nCnt++;
|
|
|
- nPos = value.IndexOf(separator, nPos + nSepLen, StringComparison.Ordinal);
|
|
|
- }
|
|
|
- return nCnt + 1;
|
|
|
-
|
|
|
- }
|
|
|
- public static string SeparateValue(this string value, string separator, int zeroBasedIndex)
|
|
|
- {
|
|
|
- var nSepLen = separator.Length;
|
|
|
- var nStrLen = value.Length;
|
|
|
- if (nStrLen == 0 || nSepLen == 0 || nSepLen > nStrLen)
|
|
|
- throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
|
|
|
- var nLastPos = 0;
|
|
|
- var nPos = value.IndexOf(separator, StringComparison.Ordinal);
|
|
|
- while (nPos >= 0)
|
|
|
- {
|
|
|
- if (zeroBasedIndex == 0)
|
|
|
- break;
|
|
|
- nLastPos = nPos + nSepLen;
|
|
|
- nPos = value.IndexOf(separator, nPos + nSepLen, StringComparison.Ordinal);
|
|
|
- zeroBasedIndex--;
|
|
|
- }
|
|
|
- if (zeroBasedIndex > 0)
|
|
|
- throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
|
|
|
- return value.Substring(nLastPos, nPos == -1 ? nStrLen - nLastPos : nPos - nLastPos);
|
|
|
- }
|
|
|
- public static string SeparateValueLast(this string value, string separator)
|
|
|
- {
|
|
|
- var nSepLen = separator.Length;
|
|
|
- var nStrLen = value.Length;
|
|
|
- if (nStrLen == 0 || nSepLen == 0 || nSepLen > nStrLen)
|
|
|
- throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
|
|
|
- return value.SeparateValue(separator, value.SeparateValueCount(separator) - 1);
|
|
|
- }
|
|
|
- public static string RemoveLast(this string value)
|
|
|
- {
|
|
|
- return value.Length == 0 ? string.Empty : value.Substring(0, value.Length - 1);
|
|
|
- }
|
|
|
- public static string RemoveFirst(this string value)
|
|
|
- {
|
|
|
- return value.Length == 0 ? string.Empty : value.Substring(1, value.Length - 1);
|
|
|
- }
|
|
|
-
|
|
|
- public static int MaxLineChars(this string value)
|
|
|
- {
|
|
|
- var aParts = value.Split("/n");
|
|
|
- return aParts.Length > 0 ? aParts.Max(x => x.Length) : value.Length;
|
|
|
- }
|
|
|
-
|
|
|
- public static string[] Split(this string value, string separator)
|
|
|
- {
|
|
|
- return value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
- }
|
|
|
-
|
|
|
- public static Tuple<string, string>[] Extract(this string value, params string[] hashes) //hashtype, tashname
|
|
|
- {
|
|
|
-
|
|
|
- var oResult = new List<Tuple<string, string>>();
|
|
|
- if (hashes.Length == 0)
|
|
|
- return new Tuple<string, string>[0];
|
|
|
- var nLenIndex = value.Length + 1;
|
|
|
- foreach (var sHash in hashes)
|
|
|
- {
|
|
|
- var bBeginBlock = true;
|
|
|
- var nPos = value.IndexOf(sHash, StringComparison.InvariantCulture);
|
|
|
- var nLenHash = sHash.Length;
|
|
|
- if (nPos < 0)
|
|
|
- continue;
|
|
|
- while (nPos >= 0)
|
|
|
- {
|
|
|
- if (nPos + nLenHash > nLenIndex)
|
|
|
- break;
|
|
|
- var nEndPos = value.IndexOf(sHash, nPos + nLenHash, StringComparison.InvariantCulture);
|
|
|
- if (nEndPos < 0)
|
|
|
- break;
|
|
|
- if (bBeginBlock)
|
|
|
- {
|
|
|
- var oEntry = new Tuple<string, string>(sHash, value.Substring(nPos + nLenHash, nEndPos - nPos - nLenHash));
|
|
|
- if (!oResult.Contains(oEntry))
|
|
|
- oResult.Add(oEntry);
|
|
|
- }
|
|
|
- nPos = nEndPos;
|
|
|
- bBeginBlock = !bBeginBlock;
|
|
|
- }
|
|
|
- }
|
|
|
- return oResult.ToArray();
|
|
|
- }
|
|
|
-
|
|
|
- public static string EnsurePathBackslash(this string value)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(value))
|
|
|
- return value;
|
|
|
- value = value.Trim();
|
|
|
- if (value.EndsWith(Path.DirectorySeparatorChar))
|
|
|
- return value;
|
|
|
- return value + Path.DirectorySeparatorChar;
|
|
|
- }
|
|
|
- public static string EnsurePathNonBackslash(this string value)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(value))
|
|
|
- return value;
|
|
|
- value = value.Trim();
|
|
|
- if (!value.EndsWith(Path.DirectorySeparatorChar))
|
|
|
- return value;
|
|
|
- return value.RemoveLast();
|
|
|
- }
|
|
|
-
|
|
|
- public static string RemoveWhitespace(this string input)
|
|
|
- {
|
|
|
- return new string(input.ToCharArray()
|
|
|
- .Where(c => !Char.IsWhiteSpace(c))
|
|
|
- .ToArray());
|
|
|
- }
|
|
|
-
|
|
|
+ /// </summary>
|
|
|
+ public static IEqualityComparer<string> DefaultLikeComparer { get; set; } = new StringPatternComparer();
|
|
|
+
|
|
|
+ public static string Align(this string value, int size)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(value)) return new string(' ', size);
|
|
|
+ if (value.Length < size)
|
|
|
+ return value + new string(' ', size - value.Length);
|
|
|
+ return Truncate(value, size);
|
|
|
+ }
|
|
|
+ public static string Truncate(this string value, int maxLength)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(value)) return value;
|
|
|
+ return value.Length <= maxLength ? value : value.Substring(0, maxLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int SeparateValueCount(this string value, string separator)
|
|
|
+ {
|
|
|
+ var nSepLen = separator.Length;
|
|
|
+ var nStrLen = value.Length;
|
|
|
+ if (nStrLen == 0 || nSepLen == 0)
|
|
|
+ return nSepLen == 0 && nStrLen > 0 ? 1 : 0;
|
|
|
+ var nCnt = 0;
|
|
|
+ var nPos = value.IndexOf(separator, StringComparison.Ordinal);
|
|
|
+ while (nPos >= 0)
|
|
|
+ {
|
|
|
+ nCnt++;
|
|
|
+ nPos = value.IndexOf(separator, nPos + nSepLen, StringComparison.Ordinal);
|
|
|
+ }
|
|
|
+ return nCnt + 1;
|
|
|
+
|
|
|
+ }
|
|
|
+ public static string SeparateValue(this string value, string separator, int zeroBasedIndex)
|
|
|
+ {
|
|
|
+ var nSepLen = separator.Length;
|
|
|
+ var nStrLen = value.Length;
|
|
|
+ if (nStrLen == 0 || nSepLen == 0 || nSepLen > nStrLen)
|
|
|
+ throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
|
|
|
+ var nLastPos = 0;
|
|
|
+ var nPos = value.IndexOf(separator, StringComparison.Ordinal);
|
|
|
+ while (nPos >= 0)
|
|
|
+ {
|
|
|
+ if (zeroBasedIndex == 0)
|
|
|
+ break;
|
|
|
+ nLastPos = nPos + nSepLen;
|
|
|
+ nPos = value.IndexOf(separator, nPos + nSepLen, StringComparison.Ordinal);
|
|
|
+ zeroBasedIndex--;
|
|
|
+ }
|
|
|
+ if (zeroBasedIndex > 0)
|
|
|
+ throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
|
|
|
+ return value.Substring(nLastPos, nPos == -1 ? nStrLen - nLastPos : nPos - nLastPos);
|
|
|
+ }
|
|
|
+ public static string SeparateValueLast(this string value, string separator)
|
|
|
+ {
|
|
|
+ var nSepLen = separator.Length;
|
|
|
+ var nStrLen = value.Length;
|
|
|
+ if (nStrLen == 0 || nSepLen == 0 || nSepLen > nStrLen)
|
|
|
+ throw new IndexOutOfRangeException("SeparateValue method has index out of range.");
|
|
|
+ return value.SeparateValue(separator, value.SeparateValueCount(separator) - 1);
|
|
|
+ }
|
|
|
+ public static string RemoveLast(this string value)
|
|
|
+ {
|
|
|
+ return value.Length == 0 ? string.Empty : value.Substring(0, value.Length - 1);
|
|
|
+ }
|
|
|
+ public static string RemoveFirst(this string value)
|
|
|
+ {
|
|
|
+ return value.Length == 0 ? string.Empty : value.Substring(1, value.Length - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int MaxLineChars(this string value)
|
|
|
+ {
|
|
|
+ var aParts = value.Split("/n");
|
|
|
+ return aParts.Length > 0 ? aParts.Max(x => x.Length) : value.Length;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string[] Split(this string value, string separator)
|
|
|
+ {
|
|
|
+ return value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Tuple<string, string>[] Extract(this string value, params string[] hashes) //hashtype, tashname
|
|
|
+ {
|
|
|
+
|
|
|
+ var oResult = new List<Tuple<string, string>>();
|
|
|
+ if (hashes.Length == 0)
|
|
|
+ return new Tuple<string, string>[0];
|
|
|
+ var nLenIndex = value.Length + 1;
|
|
|
+ foreach (var sHash in hashes)
|
|
|
+ {
|
|
|
+ var bBeginBlock = true;
|
|
|
+ var nPos = value.IndexOf(sHash, StringComparison.InvariantCulture);
|
|
|
+ var nLenHash = sHash.Length;
|
|
|
+ if (nPos < 0)
|
|
|
+ continue;
|
|
|
+ while (nPos >= 0)
|
|
|
+ {
|
|
|
+ if (nPos + nLenHash > nLenIndex)
|
|
|
+ break;
|
|
|
+ var nEndPos = value.IndexOf(sHash, nPos + nLenHash, StringComparison.InvariantCulture);
|
|
|
+ if (nEndPos < 0)
|
|
|
+ break;
|
|
|
+ if (bBeginBlock)
|
|
|
+ {
|
|
|
+ var oEntry = new Tuple<string, string>(sHash, value.Substring(nPos + nLenHash, nEndPos - nPos - nLenHash));
|
|
|
+ if (!oResult.Contains(oEntry))
|
|
|
+ oResult.Add(oEntry);
|
|
|
+ }
|
|
|
+ nPos = nEndPos;
|
|
|
+ bBeginBlock = !bBeginBlock;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return oResult.ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string EnsurePathBackslash(this string value)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(value))
|
|
|
+ return value;
|
|
|
+ value = value.Trim();
|
|
|
+ if (value.EndsWith(Path.DirectorySeparatorChar))
|
|
|
+ return value;
|
|
|
+ return value + Path.DirectorySeparatorChar;
|
|
|
+ }
|
|
|
+ public static string EnsurePathNonBackslash(this string value)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(value))
|
|
|
+ return value;
|
|
|
+ value = value.Trim();
|
|
|
+ if (!value.EndsWith(Path.DirectorySeparatorChar))
|
|
|
+ return value;
|
|
|
+ return value.RemoveLast();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string RemoveWhitespace(this string input)
|
|
|
+ {
|
|
|
+ return new string(input.ToCharArray()
|
|
|
+ .Where(c => !Char.IsWhiteSpace(c))
|
|
|
+ .ToArray());
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Performs like operator on input string. customComparer can be provides to override default comparer.
|
|
|
/// </summary>
|
|
|
/// <param name="input">Input string to compare</param>
|
|
|
/// <param name="searchPattern">Search pattern corresponds with comparer implementation.</param>
|
|
|
/// <param name="customComparer">Custom comparer. If not set, use default comparer (defined in <see cref="DefaultLikeComparer"/>)</param>
|
|
|
- /// <returns></returns>
|
|
|
- public static bool Like(this string input,string searchPattern, IEqualityComparer<string>? customComparer = null)
|
|
|
- {
|
|
|
- customComparer ??= DefaultLikeComparer;
|
|
|
- return customComparer.Equals(input, searchPattern);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+ /// <returns></returns>
|
|
|
+ public static bool Like(this string input, string searchPattern, IEqualityComparer<string>? customComparer = null)
|
|
|
+ {
|
|
|
+ customComparer ??= DefaultLikeComparer;
|
|
|
+ return customComparer.Equals(input, searchPattern);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Splits a string by the specified separator, but ignores separators that are within brackets/quotes.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="value">The string to split</param>
|
|
|
+ /// <param name="separator">The separator string to split on</param>
|
|
|
+ /// <param name="beginBracket">The opening bracket/quote character(s)</param>
|
|
|
+ /// <param name="endBracket">The closing bracket/quote character(s). If null, uses the same as beginBracket</param>
|
|
|
+ /// <param name="removeEmptyItems">If true, removes empty entries from the result</param>
|
|
|
+ /// <param name="trimValues">If true, trims whitespace from each result value</param>
|
|
|
+ /// <param name="keepBracket">If true, keeps the begin and end brackets in the result</param>
|
|
|
+ /// <returns>Array of split string parts</returns>
|
|
|
+ public static string[] SplitQuoted(this string value, string separator, string beginBracket, string? endBracket = null, bool removeEmptyItems = true, bool trimValues = true, bool keepBracket = false)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(value))
|
|
|
+ return new string[] { value ?? string.Empty };
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(separator))
|
|
|
+ throw new ArgumentException("Separator cannot be null or empty", nameof(separator));
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(beginBracket))
|
|
|
+ throw new ArgumentException("Begin bracket cannot be null or empty", nameof(beginBracket));
|
|
|
+
|
|
|
+ // If endBracket is not specified, use the same as beginBracket
|
|
|
+ endBracket ??= beginBracket;
|
|
|
+
|
|
|
+ var result = new List<string>();
|
|
|
+ var currentPart = new StringBuilder();
|
|
|
+ var i = 0;
|
|
|
+
|
|
|
+ while (i < value.Length)
|
|
|
+ {
|
|
|
+ // Check if we're at the start of a separator
|
|
|
+ if (i <= value.Length - separator.Length &&
|
|
|
+ value.Substring(i, separator.Length) == separator)
|
|
|
+ {
|
|
|
+ // Found separator outside brackets, so add current part to result
|
|
|
+ result.Add(currentPart.ToString());
|
|
|
+ currentPart.Clear();
|
|
|
+ i += separator.Length;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if we're at the start of a begin bracket
|
|
|
+ if (i <= value.Length - beginBracket.Length &&
|
|
|
+ value.Substring(i, beginBracket.Length) == beginBracket)
|
|
|
+ {
|
|
|
+ // Found begin bracket, add it to current part if keepBracket is true
|
|
|
+ if (keepBracket)
|
|
|
+ {
|
|
|
+ currentPart.Append(beginBracket);
|
|
|
+ }
|
|
|
+ i += beginBracket.Length;
|
|
|
+
|
|
|
+ // Now find the matching end bracket
|
|
|
+ var bracketDepth = 1;
|
|
|
+ while (i < value.Length && bracketDepth > 0)
|
|
|
+ {
|
|
|
+ // Check for end brackets first (important for single-character brackets like quotes)
|
|
|
+ if (i <= value.Length - endBracket.Length &&
|
|
|
+ value.Substring(i, endBracket.Length) == endBracket)
|
|
|
+ {
|
|
|
+ i += endBracket.Length;
|
|
|
+ bracketDepth--;
|
|
|
+ // Add the end bracket if we're still nested or if keepBracket is true for outermost
|
|
|
+ if (bracketDepth > 0 || keepBracket)
|
|
|
+ {
|
|
|
+ currentPart.Append(endBracket);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Check for nested begin brackets (only if different from end bracket or if we haven't found end bracket)
|
|
|
+ else if (beginBracket != endBracket &&
|
|
|
+ i <= value.Length - beginBracket.Length &&
|
|
|
+ value.Substring(i, beginBracket.Length) == beginBracket)
|
|
|
+ {
|
|
|
+ currentPart.Append(beginBracket);
|
|
|
+ i += beginBracket.Length;
|
|
|
+ bracketDepth++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Regular character inside brackets
|
|
|
+ currentPart.Append(value[i]);
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Regular character outside brackets
|
|
|
+ currentPart.Append(value[i]);
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add the last part
|
|
|
+ result.Add(currentPart.ToString());
|
|
|
+
|
|
|
+ // Process the result based on options
|
|
|
+ var finalResult = new List<string>();
|
|
|
+ foreach (var part in result)
|
|
|
+ {
|
|
|
+ var processedPart = trimValues ? part.Trim() : part;
|
|
|
+
|
|
|
+ if (!removeEmptyItems || !string.IsNullOrEmpty(processedPart))
|
|
|
+ {
|
|
|
+ finalResult.Add(processedPart);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return finalResult.ToArray();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|