using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using System; using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace Sarand.Net.Standard.Base.Utils { //======================================================================= public static class StringUtil { //------------------------------------------------------------------- // Definitions. public static Regex RegEx_Whitespace = new Regex("\\s+"); public static Regex RegEx_Whitespace_Match = new Regex("^\\s+$"); public static Regex RegEx_LineDelimiters = new Regex(@"\r\n?|\n"); //------------------------------------------------------------------- // Properties. //------------------------------------------------------------------- public static int GetLength(String value) { if (String.IsNullOrEmpty(value)) return 0; return value.Length; } /// Test for null or empty value. public static bool IsEmpty(this string value) { return String.IsNullOrEmpty(value); } /// Test for null, empty or a value that consists of whitespace characters only. public static bool IsBlank(this string value) { return String.IsNullOrEmpty(value) || Match(value, RegEx_Whitespace_Match); } //------------------------------------------------------------------- // Comparison. //------------------------------------------------------------------- public static bool EqualsCI(this String value1, String value2) { return String.Equals(value1, value2, StringComparison.OrdinalIgnoreCase); } public static bool StartsWithCI(this String value1, String value2) { if (IsEmpty(value1) || IsEmpty(value2)) return false; return (value1.StartsWith(value2, StringComparison.OrdinalIgnoreCase)); } public static bool EndsWithCI(this String value1, String value2) { if (IsEmpty(value1) || IsEmpty(value2)) return false; return (value1.EndsWith(value2, StringComparison.OrdinalIgnoreCase)); } public static bool ContainsCI(this String value1, String value2) { if (IsEmpty(value1) || IsEmpty(value2)) return false; return (value1.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0); } //------------------------------------------------------------------- // Regular expressions. //------------------------------------------------------------------- /// /// Create Regex from (quoted) expression and apply Regex.Match to String value and return Match.Success. /// - Case sensitive. /// public static bool Match(this String value, String expression) { return value.Match(expression, RegexOptions.None); } /// /// Create Regex from (quoted) expression and apply Regex.Match to String value and return Match.Success. /// - Case insensitive. /// public static bool MatchCI(this String value, String expression) { return value.Match(expression, RegexOptions.IgnoreCase); } /// /// Create Regex from (quoted) expression and apply Regex.Match to String value and return Match.Success. /// - Supply options as required. /// public static bool Match(this String value, String expression, RegexOptions options) { return new Regex(Regex.Escape(expression), options).Match(value).Success; } /// /// Apply Regex.Match to String value and return Match.Success. /// public static bool Match(this String value, Regex re) { return re.Match(value).Success; } /// /// Apply Regex.Matches to String value and return true if any matches found. /// public static bool Matches(this String value, Regex re) { return re.Matches(value).Count < 1; } //------------------------------------------------------------------- // Alternative Content. //------------------------------------------------------------------- public static String EmptyOnNull(String value) { return value ?? String.Empty; } /// Return the first value in the list that is not empty. Default if all are empty is empty string. public static String OnEmpty(params String[] values) { foreach (String value in values) if (!IsEmpty(value)) return value; return ""; } /// Return the first value in the list that is not blank. Default if all are blank is empty string. public static String OnBlank(params String[] values) { foreach (String value in values) if (!IsBlank(value)) return value; return ""; } //------------------------------------------------------------------- // C o n t e n t //------------------------------------------------------------------- public static bool IsAlpha(this string value) { return EmptyOnNull(value).All(Char.IsLetter); } public static bool IsAlphaNumeric(this string value) { return EmptyOnNull(value).All(Char.IsLetterOrDigit); } public static bool IsNumeric(this string value, bool allowNegative = false) { Int64 number = 0; if (!Int64.TryParse(value, out number)) return false; return (allowNegative || number >= 0); } public static bool IsDecimal(this string value, bool allowNegative = false) { Decimal number = 0; if (!Decimal.TryParse(value, out number)) return false; return (allowNegative || number >= 0); } //------------------------------------------------------------------- /// Test for Hex character. public static bool IsHex(this char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } /// Test for Hex string. public static bool IsHex(this String value) { if (IsEmpty(value)) return false; foreach (char c in value) if (!IsHex(c)) return false; return true; } //------------------------------------------------------------------- // P a d d i n g //------------------------------------------------------------------- public static String PadLeft(this String value, int length, char pad = ' ') { value = StringUtil.EmptyOnNull(value); while (value.Length < length) value = pad + value; return value; } public static String PadRight(this String value, int length, char pad = ' ') { value = StringUtil.EmptyOnNull(value); while (value.Length < length) value = value + pad; return value; } //------------------------------------------------------------------- // C o n v e r t //------------------------------------------------------------------- public static String CapitaliseFirst(this String value, TextInfo textInfo = null) { textInfo = (textInfo ?? CultureInfo.CurrentCulture.TextInfo); if (StringUtil.IsEmpty(value)) return value; return textInfo.ToUpper(value.First()) + value.Substring(1); } public static string ToTitleCase(this string value, TextInfo textInfo = null) { textInfo = (textInfo ?? CultureInfo.CurrentCulture.TextInfo); String[] tokens = value.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < tokens.Length; i++) { String token = tokens[i]; tokens[i] = textInfo.ToUpper(token.First()) + token.Substring(1); } return string.Join(" ", tokens); } //------------------------------------------------------------------- // C l e a n s e //------------------------------------------------------------------- /// Trim leading, trailling and multiple internal spaces. public static String TrimAll(this String value) { if (IsEmpty(value)) return value; value = value.Trim().TrimInternalSpaces(); return value; } /// /// Trim any combination of the specified characters from the start, /// end and where more than one found together internally, /// replacing with the specified string. /// /// /// /// public static String TrimAll(this String value, char[] trimChars, String replacement = " ") { if (IsEmpty(value) || trimChars.Length < 1) return value; // Trim leading and trailing. value = value.Trim(trimChars); // Use regex to replace multiple internal. Regex regexMultipleInternal = new Regex("[" + Regex.Escape(new String(trimChars)) + "]{2,}", RegexOptions.None); return regexMultipleInternal.Replace(value, replacement); } private static Regex regexMultipleSpaces = new Regex("[ ]{2,}", RegexOptions.Compiled); public static String TrimInternalSpaces(this String value) { return regexMultipleSpaces.Replace(value, " "); } //------------------------------------------------------------------- // R e p l a c e //------------------------------------------------------------------- /// Case-insensitive String replacement, invariant culture. public static String ReplaceCI(this String value, String search, String replace) { return value.ReplaceCI(search, replace, CultureInfo.InvariantCulture); } /// Case-insensitive String replacement. public static String ReplaceCI(this String value, String search, String replace, CultureInfo cultureInfo) { return value.Replace(search, replace, true, cultureInfo); } /// Replace any of the characters in the search string with the specified replacement character. public static string ReplaceAny(this string value, String searchChars, char replaceChar) { for (int i = 0; i < searchChars.Length; i++) value = value.Replace(searchChars[i], replaceChar); return value; } /// /// Replace all occurences of any of the search strings with the replace string, even where search values remain after the each replacement. /// NB Use String.Replace if the replacement contains the search string /// public static string ReplaceAll(this string value, String search, String replace) { return value.ReplaceAll(new String[] { search }, replace); } public static string ReplaceAll(this string value, String[] searches, String replace) { foreach (String search in searches) { while (value.Contains(search)) { value = value.Replace(search, replace); if (value.Contains(search) && replace.Contains(search)) throw new NotSupportedException("StringUtil.ReplaceAll Replace value cannot contain search value"); } } return value; } /// Duplicate every instance of the specified character. public static String Duplicate(this String value, char search) { return value.Replace(search.ToString(), search.ToString() + search.ToString()); } //------------------------------------------------------------------- /// /// Replace all line delimiters (\r\n or \n). /// /// /// /// public static String ConsolidateLineDelimiters(this String value, String replace = "\n") { return RegEx_LineDelimiters.Replace(value, replace); } //------------------------------------------------------------------- // E x t r a c t i o n //------------------------------------------------------------------- public static string Left(this string value, int length) { if (String.IsNullOrEmpty(value)) return value; if (length < 0) return String.Empty; return (length >= value.Length ? value : value.Substring(0, length)); } public static string Right(this string value, int length) { if (String.IsNullOrEmpty(value)) return value; if (length < 0) return String.Empty; return ( length >= value.Length ? value : value.Substring(value.Length - length) ); } /// /// Safe SubString - avoinding out of bounds exceptions etc. /// /// /// /// /// public static string SubstringSafe(this string value, int start, int length = -1) { if (String.IsNullOrEmpty(value)) return String.Empty; start = Math.Max(0, start); if (start >= value.Length) return String.Empty; value = value.Substring(start); if (length < 0 || length > value.Length) return value; return value.Substring(0, length); } //------------------------------------------------------------------- /// Return the common previx for the values in the list. public static String GetPrefix(params String[] values) { String prefix = values.Any() ? values[0] : ""; foreach (String value in values) { // Determine how much o fthe prefix matches this value. for (int i = 0; i < prefix.Length; i++) { if (value.Length <= i || value[i] != prefix[i]) prefix = prefix.Substring(0, i); } } return prefix; } //------------------------------------------------------------------- /// /// Concatenate the partial path values, stripping leading and trailing slashes, and converting slashes (one or more in sequence) to a single path delimiter. /// public static String CombineFilePath(params String[] values) { return CombineFilePath(Path.DirectorySeparatorChar, values); } /// /// Concatenate the partial path values with forward slashes ('/'), stripping leading and trailing slashes and converting slashes (one or more in sequence) to a single forward slash. /// public static String CombineForwardSlashDelimited(params String[] values) { return CombineFilePath('/', values); } /// /// Concatenate the partial path values with the specified dlimiter, stripping leading and trailing slashes and delimiters, and converting slashes and delimiters to the required delimiter. /// private static String CombineFilePath(char delim, params String[] values) { String path = ""; String delimPattern = "[\\\\\\/" + (delim != '\\' && delim != '/' ? "\\" + delim : "") + "]"; foreach (String value in values) { if (IsBlank(path)) // Path not yet started - being with this value. path = value; else // Path is already started. // Remove trailing delimiters from path and leading delimiters from next partial value before concatenating with path separator. path = Regex.Replace(path, delimPattern + "+$", "") + delim.ToString() + Regex.Replace(value, "^" + delimPattern + "+", ""); } // Convert any combination of the alternative delimiters with requested delimiter, excluding start of path, eg. '\\share\...'. return Regex.Replace(path, delimPattern, delim.ToString()); } //------------------------------------------------------------------- // C o n v e r s i o n //------------------------------------------------------------------- // Integer values. public static short ToShort(this string value, short defaultValue = 0) { return (short)ToLong(value, defaultValue); } public static int ToInt(this string value, int defaultValue = 0) { return (int)ToLong(value, defaultValue); } public static long ToLong(this string value, long defaultValue = 0) { if (String.IsNullOrEmpty(value)) return defaultValue; try { Double returnValue = defaultValue; if (Double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out returnValue)) return (Int64)returnValue; } catch (Exception) { } return defaultValue; } public static short ToShort(Object value, short defaultValue = 0) { return (short)ToLong(value, defaultValue); } public static int ToInt(Object value, int defaultValue = 0) { return (int)ToLong(value, defaultValue); } public static long ToLong(Object value, long defaultValue = 0) { if (value == null) return defaultValue; try { return ToLong(value.ToString()); } catch (Exception) { } return defaultValue; } //--------------------------------------------------------------------------- // SysId. public static SysId ToSysId(this String value) { return new SysId(StringUtil.ToLong(value)); } //--------------------------------------------------------------------------- // Hex values to integer. /// Convert hex character to integer value. public static byte ToHex(this char value) { if (value >= '0' && value <= '9') return (byte)(value - '0'); if (value >= 'A' && value <= 'F') return (byte)(value - 'A' + 10); if (value >= 'a' && value <= 'f') return (byte)(value - 'a' + 10); return 0; } /// Convert pair of hex character to integer value. public static byte ToHex(char cHex1, char cHex2) { return (byte)((ToHex(cHex1) * 16) + ToHex(cHex2)); } /// Convert hex strings to integer value. public static ulong ToHex(this String value) { if (IsEmpty(value)) return 0; ulong intValue = 0; foreach (char c in value) { // Shift previous value half a byte (nibble) - equivalent to single hex character. intValue <<= 4; // Get value of next hex character. byte charValue = ToHex(c); intValue += charValue; } return intValue; } //------------------------------------------------------------------- // Floating point values. public static float ToSingle(this string value, float defaultValue = 0) { return Convert.ToSingle(ToDouble(value, defaultValue)); } public static double ToDouble(this string value, double defaultValue = 0) { if (String.IsNullOrEmpty(value)) return defaultValue; try { Double returnValue = defaultValue; if (Double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out returnValue)) return returnValue; } catch (Exception) { } return defaultValue; } public static float ToSingle(Object value, float defaultValue = 0) { return Convert.ToSingle(ToDouble(value, defaultValue)); } public static double ToDouble(Object value, double defaultValue = 0) { if (value == null) return defaultValue; try { return ToDouble(value.ToString()); } catch (Exception) { } return defaultValue; } //------------------------------------------------------------------- // Decimal values. public static decimal ToDecimal(this string value, decimal defaultValue = 0) { if (String.IsNullOrEmpty(value)) return defaultValue; try { Decimal returnValue = defaultValue; if (Decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out returnValue)) return returnValue; } catch (Exception) { } return defaultValue; } public static decimal ToDecimal(Object value, decimal defaultValue = 0) { if (value == null) return defaultValue; try { return ToDecimal(value.ToString()); } catch (Exception) { } return defaultValue; } //------------------------------------------------------------------- // Boolean values. public static bool ToBool(this string value) { return value.IsTrue(); } public static bool IsTrue(this string value) { if (String.IsNullOrEmpty(value)) return false; if (value.ToLong() != 0) return true; if (value.ToDouble() != 0.0) return true; if (value.Left(1).EqualsCI("Y")) return true; if (value.EqualsCI("True")) return true; return false; } //------------------------------------------------------------------- // F o r m a t t i n g //------------------------------------------------------------------- public static String FormatNumeric(double numericValue, ushort decimalPlaces = 0, ushort sigFigs = 0, char thousandsDelim = '\0', char decimalDelim = '.') { String value = String.Empty; try { String format = "F" + decimalPlaces.ToString(); if (thousandsDelim != '\0') format = "N" + decimalPlaces.ToString(); value = numericValue.ToString(format); } catch (Exception exc) { SaLogger.Debug("StringUtil.FormatNumeric(double) " + exc.GetType().Name + ": " + exc.Message); } return value; } public static String FormatNumeric(decimal numericValue, ushort decimalPlaces = 0, ushort sigFigs = 0, char thousandsDelim = '\0', char decimalDelim = '.') { String value = String.Empty; try { String format = "F" + decimalPlaces.ToString(); if (thousandsDelim != '\0') format = "N" + decimalPlaces.ToString(); value = numericValue.ToString(format); } catch (Exception exc) { SaLogger.Debug("StringUtil.FormatNumeric(decimal) " + exc.GetType().Name + ": " + exc.Message); } return value; } //------------------------------------------------------------------- // Specialist finding. public static int FindMatchingBrace(this String value, int braceOpenPos) { if (String.IsNullOrEmpty(value) || braceOpenPos < 0 || braceOpenPos >= value.Length) return -1; char sourceBrace = value[braceOpenPos]; char targetBrace = '\0'; // Target character to be found. bool openBraceFound = false; // Identifies forward or backwards search (close or open bracket to be found). // Identify the target bracket character: switch (sourceBrace) { case '(': targetBrace = ')'; openBraceFound = true; break; case ')': targetBrace = '('; break; case '[': targetBrace = ']'; openBraceFound = true; break; case ']': targetBrace = '['; break; case '{': targetBrace = '}'; openBraceFound = true; break; case '}': targetBrace = '{'; break; case '<': targetBrace = '>'; openBraceFound = true; break; case '>': targetBrace = '<'; break; } // Verify that a valid bracket has been found: if (targetBrace == 0) return -1; // Start search from the 'source' bracket position: int braceCount = 1; // Search continues until this count gets back down to '0'. int searchPos = braceOpenPos; while (true) { // Navigate to next character to be checked (direction depends on whether target is open or close bracket): if (openBraceFound) searchPos++; else searchPos--; if (searchPos < 0 || searchPos >= value.Length) return -1; // If character at this position is the 'source' bracket, increment count (another level of embedded brackets): char searchChar = value[searchPos]; if (searchChar == sourceBrace) braceCount++; else if (searchChar == targetBrace) { braceCount--; if (braceCount == 0) return searchPos; // Matching bracket found. else if (braceCount < 0) return -1; // Invalid combination of brackets. } } //return -1; Unreachable code! } //------------------------------------------------------------------- } //======================================================================= }