using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using ICD.Common.Properties; using ICD.Common.Utils.Extensions; namespace ICD.Common.Utils { public static class StringUtils { private const ushort ASCII_READABLE_START = 32; private const ushort ASCII_READABLE_END = 126; /// /// Returns the number as a string in the format "\\x00" /// /// /// [PublicAPI] public static string ToHexLiteral(int numeric) { return string.Format("\\x{0:X2}", numeric); } /// /// Returns the character as a string in the format "\\x00" /// /// /// [PublicAPI] public static string ToHexLiteral(char c) { return ToHexLiteral(Convert.ToInt32(c)); } /// /// Returns a string as a string in the format "\\x00\\x00..." /// /// /// [PublicAPI] public static string ToHexLiteral(string input) { if (input == null) throw new ArgumentNullException("input"); StringBuilder builder = new StringBuilder(); foreach (string literal in input.Select(c => ToHexLiteral(c))) builder.Append(literal); return builder.ToString(); } /// /// Converts a character in the string format "\\x00" to char. /// /// /// [PublicAPI] public static char FromHexLiteralCharacter(string data) { if (data == null) throw new ArgumentNullException("data"); if (data.Length != 4) throw new ArgumentException("Expecting data in \x00 format of 4 characters", "data"); string hexValue = data.Substring(2); return (char)Convert.ToByte(hexValue, 16); } /// /// Converts a string in the format "\\x00\\x00..." to hex representation. /// /// /// [PublicAPI] public static string FromHexLiteral(string data) { if (data == null) throw new ArgumentNullException("data"); StringBuilder builder = new StringBuilder(); foreach (char item in data.Split(4).Select(s => FromHexLiteralCharacter(s))) builder.Append(item); return builder.ToString(); } /// /// Converts the char to a human readable character, otherwise returns a hex string /// in the format "\x00" /// /// /// [PublicAPI] public static string ToMixedReadableHexLiteral(char c) { int numeric = Convert.ToInt32(c); if (numeric >= ASCII_READABLE_START && numeric <= ASCII_READABLE_END) return c.ToString(); return ToHexLiteral(c); } /// /// Converts the input string to a string in the format "\x00\x00" with human /// readable characters where possible e.g. "Hello World!x\0D" /// /// /// [PublicAPI] public static string ToMixedReadableHexLiteral(string input) { if (input == null) throw new ArgumentNullException("input"); StringBuilder builder = new StringBuilder(); foreach (string item in input.Select(c => ToMixedReadableHexLiteral(c))) builder.Append(item); return builder.ToString(); } /// /// Uses String.Format to properly handle null values. /// /// /// public static string ToString(object value) { return string.Format("{0}", value); } /// /// Converts bytes to an ascii string. /// /// /// [PublicAPI] public static string ToString(IEnumerable bytes) { if (bytes == null) throw new ArgumentNullException("bytes"); byte[] cast = bytes as byte[] ?? bytes.ToArray(); return Encoding.GetEncoding(28591).GetString(cast, 0, cast.Length); } /// /// Converts bytes to an ascii string. /// /// /// /// [PublicAPI] public static string ToString(IEnumerable bytes, int length) { if (bytes == null) throw new ArgumentNullException("bytes"); byte[] cast = bytes as byte[] ?? bytes.ToArray(); return Encoding.GetEncoding(28591).GetString(cast, 0, length); } /// /// Converts an ascii string to bytes. /// /// /// [PublicAPI] public static byte[] ToBytes(string input) { if (input == null) throw new ArgumentNullException("input"); return Encoding.GetEncoding(28591).GetBytes(input); } /// /// Attempts to parse the string as an integer. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out int result) { return TryConvert(Convert.ToInt32, value, out result); } /// /// Attempts to parse the string as an unsigned integer. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out uint result) { return TryConvert(Convert.ToUInt32, value, out result); } /// /// Attempts to parse the string as a short integer. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out short result) { return TryConvert(Convert.ToInt16, value, out result); } /// /// Attempts to parse the string as an unsigned short integer. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out ushort result) { return TryConvert(Convert.ToUInt16, value, out result); } /// /// Attempts to parse the string as a long integer. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out long result) { return TryConvert(Convert.ToInt64, value, out result); } /// /// Attempts to parse the string as an unsigned long integer. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out ulong result) { return TryConvert(Convert.ToUInt64, value, out result); } /// /// Attempts to parse the string as a float. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out float result) { return TryConvert(Convert.ToSingle, value, out result); } /// /// Attempts to parse the string as a bool. /// /// /// /// [PublicAPI] public static bool TryParse(string value, out bool result) { return TryConvert(Convert.ToBoolean, value, out result); } /// /// Attempts to parse the string via the given conversion function. /// /// /// /// /// private static bool TryConvert(Func convertFunc, string value, out T result) { if (convertFunc == null) throw new ArgumentNullException("convertFunc"); result = default(T); try { result = convertFunc(value); return true; } catch (FormatException) { return false; } catch (InvalidCastException) { return false; } } /// /// Returns the object.ToString() with spaces before capital letters. /// /// /// public static string NiceName(object obj) { if (obj == null) throw new ArgumentNullException("obj"); return NiceName(obj.ToString()); } /// /// Inserts spaces before capital letters. /// /// http://stackoverflow.com/questions/4488969/split-a-string-by-capital-letters /// /// /// public static string NiceName(string name) { if (name == null) throw new ArgumentNullException("name"); Regex regex = new Regex(@" (?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); return regex.Replace(name, " "); } /// /// String.Format({0:######}) is unreliable if the number starts with a 0. /// This method fills an input pattern #### from right to left with characters /// from the number. /// /// /// /// public static string SafeNumericFormat(string phoneFormat, string number) { if (phoneFormat == null) throw new ArgumentNullException("phoneFormat"); if (number == null) throw new ArgumentNullException("number"); phoneFormat = Reverse(phoneFormat); number = Reverse(number); StringBuilder builder = new StringBuilder(); int index = 0; foreach (char c in phoneFormat) { if (index >= number.Length) { if (c == '#') break; builder.Append(c); continue; } if (c == '#') { builder.Append(number[index]); index++; } else builder.Append(c); } return Reverse(builder.ToString()).TrimStart(); } /// /// Reverses the string. /// /// /// [PublicAPI] public static string Reverse(string input) { if (input == null) throw new ArgumentNullException("input"); char[] charArray = input.ToCharArray(); Array.Reverse(charArray); return new string(charArray); } /// /// Repeats the char the given number of times. /// /// /// /// public static string Repeat(char input, int count) { return Repeat(input.ToString(), count); } /// /// Repeats the string the given number of times. /// /// /// /// [PublicAPI] public static string Repeat(string input, int count) { if (count < 0) throw new ArgumentOutOfRangeException("count"); return input == null || count == 0 ? string.Empty : new StringBuilder().Insert(0, input, count).ToString(); } /// /// Returns each item.ToString() in the format "[item1, item2, item3...]" /// /// /// [PublicAPI] public static string ArrayFormat(IEnumerable items) { if (items == null) throw new ArgumentNullException("items"); StringBuilder builder = new StringBuilder(); builder.Append('['); bool first = true; foreach (T item in items) { if (!first) builder.Append(", "); first = false; builder.Append(ToString(item)); } builder.Append(']'); return builder.ToString(); } /// /// Given a sequence of numbers, generates a human readable list of numeric ranges. /// E.g. [1, 2, 3, 5, 6] becomes "[1-3, 5-6]". /// /// /// [PublicAPI] public static string ArrayRangeFormat(IEnumerable items) { if (items == null) throw new ArgumentNullException("items"); string[] ranges = MathUtils.GetRanges(items) .Select(r => r[0] == r[1] ? ToString(r[0]) : string.Format("{0}-{1}", r[0], r[1])) .ToArray(); return ArrayFormat(ranges); } /// /// Given a sequence of numbers, generates a human readable list of numeric ranges. /// E.g. [1, 2, 3, 5, 6] becomes "[1-3, 5-6]". /// /// /// [PublicAPI] public static string ArrayRangeFormat(IEnumerable items) { if (items == null) throw new ArgumentNullException("items"); return ArrayRangeFormat(items.Select(i => (int)i)); } /// /// Returns a pair of numbers in the format [a - b] /// /// /// /// [PublicAPI] public static string RangeFormat(object a, object b) { return string.Format("[{0} - {1}]", a, b); } /// /// Capitalizes the first character of the string. /// /// /// [PublicAPI] public static string UppercaseFirst(string input) { if (string.IsNullOrEmpty(input)) return input; return char.ToUpper(input[0]) + input.Substring(1); } /// /// Capitalizes the first character of each word. /// /// /// [PublicAPI] public static string ToTitleCase(string input) { return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input); } /// /// Formats an IPID to "0xFF" /// /// /// public static string ToIpIdString(byte ipid) { return string.Format("0x{0:X2}", ipid); } /// /// Formats "0xFF" to an IPID. /// /// /// public static byte FromIpIdString(string value) { if (value == null) throw new ArgumentNullException("value"); try { return (byte)Convert.ToInt64(value, 16); } catch (ArgumentOutOfRangeException e) { throw new FormatException(e.Message, e); } } /// /// Formats "0xFF" to an IPID. /// /// /// /// public static bool TryFromIpIdString(string value, out byte result) { if (value == null) throw new ArgumentNullException("value"); result = 0; try { result = (byte)Convert.ToInt64(value, 16); return true; } catch (ArgumentOutOfRangeException) { return false; } } /// /// Removes all whitespace from the string. /// /// /// public static string RemoveWhitespace(string text) { return text == null ? null : text.RemoveWhitespace(); } /// /// Returns true if the string is entirely whitespace characters, or empty, or null. /// /// /// public static bool IsNullOrWhitespace(string text) { if (string.IsNullOrEmpty(text)) return true; string trimmed = text.Trim(); return trimmed.Length == 0; } /// /// Returns the password as a series of *s /// /// /// public static string PasswordFormat(string password) { return password == null ? null : Repeat('*', password.Length); } /// /// Pads the given string with quotations for readable type clarity. If the string is null, returns "NULL". /// /// /// public static string ToRepresentation(string value) { return value == null ? "NULL" : string.Format("\"{0}\"", value); } /// /// Returns the items in the format "x, y, and z" /// /// /// public static string SerialComma(IEnumerable items) { if (items == null) throw new ArgumentNullException("items"); string previous = null; StringBuilder builder = new StringBuilder(); foreach (string item in items) { if (previous != null) builder.AppendFormat("{0}, ", previous); previous = item; } if (previous != null) { if (builder.Length > 0) builder.AppendFormat("and {0}", previous); else builder.Append(previous); } return builder.ToString(); } /// /// Shim of value.ToString() /// Returns null if input value is null. /// /// /// [PublicAPI, CanBeNull] public static string Trim(string value) { return value == null ? null : value.ToUpper(); } /// /// Compares the given chars for equality. /// /// /// /// /// [PublicAPI] public static bool Compare(char a, char b, bool ignoreCase) { if (ignoreCase) { a = char.ToUpper(a, CultureInfo.InvariantCulture); b = char.ToUpper(b, CultureInfo.InvariantCulture); } return a == b; } /// /// Find the longest common string between the matches. /// E.g. /// /// C:\\Workspace /// C:\\Workshop /// /// Results in /// /// C:\\Work /// /// /// /// [PublicAPI] public static string GetLongestCommonIntersectionFromStart(IEnumerable items, bool ignoreCase) { if (items == null) throw new ArgumentNullException("items"); string output = null; foreach (string item in items) { // If there is a null in the sequence that's the best match we can make if (string.IsNullOrEmpty(item)) return null; // Seed our first item if (output == null) { output = item; continue; } // Find the common substring for (int index = 0; index < output.Length; index++) { if (index >= item.Length || !Compare(output[index], item[index], ignoreCase)) { output = output.Substring(0, index); break; } } // Abandon the search if there is no common substring if (string.IsNullOrEmpty(output)) break; } return output; } /// /// Ensures the value starts and ends with a double quote. /// /// /// public static string Enquote(string value) { if (value == null) throw new ArgumentNullException("value"); if (!value.StartsWith('"')) value = '"' + value; if (!value.EndsWith('"')) value += '"'; return value; } /// /// Removes the start and trailing double quote from the string if BOTH are present. /// /// /// public static string UnEnquote(string value) { if (value == null) throw new ArgumentNullException("value"); if (value.StartsWith('"') && value.EndsWith('"')) return value.Substring(1, value.Length - 2); return value; } } }