2312 lines · 98.9 KB · C#
RawDownload
//******************************************************************************************************//  StringExtensions.cs - Gbtc////  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  02/23/2003 - J. Ritchie Carroll//       Generated original version of source code.//  01/24/2006 - J. Ritchie Carroll//       Migrated 2.0 version of source code from 1.1 source (GSF.Shared.String).//  06/01/2006 - J. Ritchie Carroll//       Added ParseBoolean function to parse strings representing boolean's that may be numeric.//  07/07/2006 - J. Ritchie Carroll//       Added GetStringSegments function to break a string up into smaller chunks for parsing.//       and/or displaying.//  08/02/2007 - J. Ritchie Carroll//       Added a CenterText method for centering strings in console applications or fixed width fonts.//  08/03/2007 - Pinal C. Patel//       Modified the CenterText method to handle multiple lines.//  08/21/2007 - Darrell Zuercher//       Edited code comments.//  09/25/2007 - J. Ritchie Carroll//       Added TitleCase function to format a string with the first letter of each word capitalized.//  04/16/2008 - Pinal C. Patel//       Made the keys of the string dictionary returned by ParseKeyValuePairs function case-insensitive.//       Added JoinKeyValuePairs overloads that does the exact opposite of ParseKeyValuePairs.//  09/19/2008 - J. Ritchie Carroll//       Converted to C# extensions.//  12/13/2008 - F. Russell Roberson//       Generalized ParseBoolean to include "Y", and "T".//       Added IndexOfRepeatedChar - Returns the index of the first character that is repeated.//       Added Reverse - Reverses the order of characters in a string.//       Added EnsureEnd - Ensures that a string ends with a specified char or string.//       Added EnsureStart - Ensures that a string begins with a specified char or string.//       Added IsNumeric - Test to see if a string only includes characters that can be interpreted as a number.//       Added TrimWithEllipsisMiddle - Adds an ellipsis in the middle of a string as it is reduced to a specified length.//       Added TrimWithEllipsisEnd - Trims a string to not exceed a fixed length and adds a ellipsis to string end.//  02/10/2009 - J. Ritchie Carroll//       Added ConvertToType overloaded extensions.//  02/17/2009 - Josh L. Patterson//       Edited Code Comments.//  09/14/2009 - Stephen C. Wills//       Added new header and license agreement.//  01/11/2010 - Galen K. Riley//      Issue fixes for unit tests://      ConvertToType - Fix to throw ArgumentNullException instead of NullReferenceException for null value//      ConvertToType - Handling failed conversions better. Calling ConvertToType<int>("\0") returns properly//      JoinKeyValuePairs - Fix to throw ArgumentNullException instead of NullReferenceException//      ReplaceCharacters - Fix to throw ArgumentNullException instead of NullReferenceException for null replacementCharacter//      RemoveCharacters - Fix to throw ArgumentNullException instead of NullReferenceException for null characterTestFunction//      ReplaceCrLfs - Fix to throw ArgumentNullException instead of NullReferenceException for null value//      RegexDecode - Fix to throw ArgumentNullException instead of NullReferenceException for null value//  12/03/2010 - J. Ritchie Carroll//      Modified ParseKeyValuePairs such that it could handle nested pairs to any needed depth.//  12/05/2010 - Pinal C. Patel//       Added an overload for ConvertToType() that takes CultureInfo as a parameter.//  12/07/2010 - Pinal C. Patel//       Updated ConvertToType() to return the type default if passed in string is null or empty.//  01/04/2011 - J. Ritchie Carroll//       Modified ConvertToType culture default to InvariantCulture for English style parsing defaults.//  01/14/2011 - J. Ritchie Carroll//       Modified JoinKeyValuePairs to delineate values that contain nested key/value pair expressions//       such that the generated expression can correctly parsed.//  03/23/2011 - J. Ritchie Carroll//       Modified ParseKeyValuePairs to optionally ignore duplicate keys (default behavior now).//       Removed overloads for ParseKeyValuePairs and JoinKeyValuePairs using optional parameters.//  08/02/2011 - Pinal C. Patel//       Added RemoveInvalidFileNameCharacters() and ReplaceInvalidFileNameCharacters() methods.//  10/17/2012 - F Russell Robertson//       Added QuoteWrap() method.//  12/14/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.ComponentModel;using System.Diagnostics.CodeAnalysis;using System.Globalization;using System.IO;using System.Linq;using System.Runtime.InteropServices;using System.Security;using System.Text;using System.Text.RegularExpressions;using System.Threading.Tasks; namespace GSF{    /// <summary>    /// Defines extension functions related to string manipulation.    /// </summary>    public static partial class StringExtensions    {        /// <summary>        /// Parses a string intended to represent a boolean value.        /// </summary>        /// <param name="value">String representing a boolean value.</param>        /// <returns>Parsed boolean value.</returns>        /// <remarks>        /// This function, unlike Boolean.Parse, correctly parses a boolean value, even if the string value        /// specified is a number (e.g., 0 or -1). Boolean.Parse expects a string to be represented as        /// "True" or "False" (i.e., Boolean.TrueString or Boolean.FalseString respectively).        /// </remarks>        public static bool ParseBoolean(this string value)        {            if (string.IsNullOrEmpty(value))                return false;             value = value.Trim();             if (value.Length > 0)            {                // Try to parse string a number                int iresult;                 if (int.TryParse(value, out iresult))                    return (iresult != 0);                 // Try to parse string as a boolean                bool bresult;                 if (bool.TryParse(value, out bresult))                    return bresult;                 char test = value.ToUpper()[0];                 return (test == 'T' || test == 'Y');            }             return false;        }         /// <summary>        /// Converts this string into the specified type.        /// </summary>        /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam>        /// <param name="value">Source string to convert to type.</param>        /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type T if conversion fails.</returns>        /// <remarks>        /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified type T,        /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same        /// <see cref="TypeConverter"/> to convert the original object to a <see cref="string"/>; see the        /// <see cref="Common.TypeConvertToString(object)"/> method for an easy way to do this.        /// </remarks>        public static T ConvertToType<T>(this string value)        {            return ConvertToType<T>(value, null);        }         /// <summary>        /// Converts this string into the specified type.        /// </summary>        /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam>        /// <param name="value">Source string to convert to type.</param>        /// <param name="culture"><see cref="CultureInfo"/> to use for the conversion.</param>        /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type T if conversion fails.</returns>        /// <remarks>        /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified type T,        /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same        /// <see cref="TypeConverter"/> to convert the original object to a <see cref="string"/>; see the        /// <see cref="Common.TypeConvertToString(object)"/> method for an easy way to do this.        /// </remarks>        public static T ConvertToType<T>(this string value, CultureInfo culture)        {            object obj = ConvertToType(value, typeof(T), culture) ?? default(T);            return (T)obj;        }         /// <summary>        /// Converts this string into the specified type.        /// </summary>        /// <param name="value">Source string to convert to type.</param>        /// <param name="type"><see cref="Type"/> to convert string to.</param>        /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type if conversion fails.</returns>        /// <remarks>        /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,        /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same        /// <see cref="TypeConverter"/> to convert the original object to a <see cref="string"/>; see the        /// <see cref="Common.TypeConvertToString(object)"/> method for an easy way to do this.        /// </remarks>        public static object ConvertToType(this string value, Type type)        {            return ConvertToType(value, type, null);        }         /// <summary>        /// Converts this string into the specified type.        /// </summary>        /// <param name="value">Source string to convert to type.</param>        /// <param name="type"><see cref="Type"/> to convert string to.</param>        /// <param name="culture"><see cref="CultureInfo"/> to use for the conversion.</param>        /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type if conversion fails.</returns>        /// <remarks>        /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,        /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same        /// <see cref="TypeConverter"/> to convert the original object to a <see cref="string"/>; see the        /// <see cref="Common.TypeConvertToString(object)"/> method for an easy way to do this.        /// </remarks>        public static object ConvertToType(this string value, Type type, CultureInfo culture)        {            // Don't proceed further if string is empty.            if (string.IsNullOrEmpty(value))                return null;             // Initialize return type if not specified.            if ((object)type == null)                throw new ArgumentNullException(nameof(type));             // Initialize culture info if not specified.            if (culture == null)                culture = CultureInfo.InvariantCulture;             try            {                // Handle booleans as a special case to allow numeric entries as well as true/false                if (type == typeof(bool))                    return value.ParseBoolean();                 // Handle objects that have type converters (e.g., Enum, Color, Point, etc.)                TypeConverter converter = TypeDescriptor.GetConverter(type);                 // ReSharper disable once AssignNullToNotNullAttribute                return converter.ConvertFromString(null, culture, value);            }            catch            {                return null;            }        }         /// <summary>        /// Converts string into a stream using the specified <paramref name="encoding"/>.        /// </summary>        /// <param name="value">Input to string to convert to a string.</param>        /// <param name="encoding">String encoding to use; defaults to <see cref="Encoding.UTF8"/>.</param>        /// <returns>String <paramref name="value"/> encoded onto a stream.</returns>        public static Stream ToStream(this string value, Encoding encoding = null)        {            MemoryStream stream = new MemoryStream();             using (StreamWriter writer = new StreamWriter(stream, encoding ?? Encoding.UTF8, 4096, true))            {                writer.Write(value);                writer.Flush();            }             stream.Position = 0;             return stream;        }         /// <summary>        /// Asynchronously converts string into a stream using the specified <paramref name="encoding"/>.        /// </summary>        /// <param name="value">Input to string to convert to a string.</param>        /// <param name="encoding">String encoding to use; defaults to <see cref="Encoding.UTF8"/>.</param>        /// <returns>String <paramref name="value"/> encoded onto a stream.</returns>        public static async Task<Stream> ToStreamAsync(this string value, Encoding encoding = null)        {            MemoryStream stream = new MemoryStream();             using (StreamWriter writer = new StreamWriter(stream, encoding ?? Encoding.UTF8, 4096, true))            {                await writer.WriteAsync(value);                await writer.FlushAsync();            }             stream.Position = 0;             return stream;        }         /// <summary>        /// Turns source string into an array of string segments - each with a set maximum width - for parsing or displaying.        /// </summary>        /// <param name="value">Input string to break up into segments.</param>        /// <param name="segmentSize">Maximum size of returned segment.</param>        /// <returns>Array of string segments as parsed from source string.</returns>        /// <remarks>Returns a single element array with an empty string if source string is null or empty.</remarks>        public static string[] GetSegments(this string value, int segmentSize)        {            if (segmentSize <= 0)                throw new ArgumentOutOfRangeException(nameof(segmentSize), "segmentSize must be greater than zero.");             if (string.IsNullOrEmpty(value))                return new[] { "" };             int totalSegments = (int)Math.Ceiling(value.Length / (double)segmentSize);            string[] segments = new string[totalSegments];             for (int x = 0; x < segments.Length; x++)            {                if (x * segmentSize + segmentSize >= value.Length)                    segments[x] = value.Substring(x * segmentSize);                else                    segments[x] = value.Substring(x * segmentSize, segmentSize);            }             return segments;        }         /// <summary>        /// Combines a dictionary of key-value pairs in to a string.        /// </summary>        /// <param name="pairs">Dictionary of key-value pairs.</param>        /// <param name="parameterDelimiter">Character that delimits one key-value pair from another (e.g. ';').</param>        /// <param name="keyValueDelimiter">Character that delimits a key from its value (e.g. '=').</param>        /// <param name="startValueDelimiter">Optional character that marks the start of a value such that value could contain other        /// <paramref name="parameterDelimiter"/> or <paramref name="keyValueDelimiter"/> characters (e.g., "{").</param>        /// <param name="endValueDelimiter">Optional character that marks the end of a value such that value could contain other        /// <paramref name="parameterDelimiter"/> or <paramref name="keyValueDelimiter"/> characters (e.g., "}").</param>        /// <returns>A string of key-value pairs.</returns>        /// <remarks>        /// Values will be escaped within <paramref name="startValueDelimiter"/> and <paramref name="endValueDelimiter"/>        /// to contain nested key/value pair expressions like the following: <c>normalKVP=-1; nestedKVP={p1=true; p2=0.001}</c>,        /// when either the <paramref name="parameterDelimiter"/> or <paramref name="keyValueDelimiter"/> are detected in the        /// value of the key/value pair.        /// </remarks>        public static string JoinKeyValuePairs(this IDictionary<string, string> pairs, char parameterDelimiter = ';', char keyValueDelimiter = '=', char startValueDelimiter = '{', char endValueDelimiter = '}')        {            if (pairs == null)                throw new ArgumentNullException(nameof(pairs));             char[] delimiters = { parameterDelimiter, keyValueDelimiter };            List<string> values = new List<string>();            string value;             foreach (string key in pairs.Keys)            {                value = pairs[key];                 if (value.IndexOfAny(delimiters) >= 0)                    value = startValueDelimiter + value + endValueDelimiter;                 values.Add($"{key}{keyValueDelimiter}{value}");            }             return string.Join(parameterDelimiter + " ", values);        }         /// <summary>        /// Parses key/value pair expressions from a string. Parameter pairs are delimited by <paramref name="keyValueDelimiter"/>        /// and multiple pairs separated by <paramref name="parameterDelimiter"/>. Supports encapsulated nested expressions.        /// </summary>        /// <param name="value">String containing key/value pair expressions to parse.</param>        /// <param name="parameterDelimiter">Character that delimits one key/value pair from another.</param>        /// <param name="keyValueDelimiter">Character that delimits key from value.</param>        /// <param name="startValueDelimiter">Optional character that marks the start of a value such that value could contain other        /// <paramref name="parameterDelimiter"/> or <paramref name="keyValueDelimiter"/> characters.</param>        /// <param name="endValueDelimiter">Optional character that marks the end of a value such that value could contain other        /// <paramref name="parameterDelimiter"/> or <paramref name="keyValueDelimiter"/> characters.</param>        /// <param name="ignoreDuplicateKeys">Flag determines whether duplicates are ignored. If flag is set to <c>false</c> an        /// <see cref="ArgumentException"/> will be thrown when all key parameters are not unique.</param>        /// <returns>Dictionary of key/value pairs.</returns>        /// <remarks>        /// <para>        /// Parses a string containing key/value pair expressions (e.g., "localPort=5001; transportProtocol=UDP; interface=0.0.0.0").        /// This method treats all "keys" as case-insensitive. Nesting of key/value pair expressions is allowed by encapsulating the        /// value using the <paramref name="startValueDelimiter"/> and <paramref name="endValueDelimiter"/> values (e.g.,         /// "dataChannel={Port=-1;Clients=localhost:8800}; commandChannel={Port=8900}; dataFormat=FloatingPoint;"). There must be one        /// <paramref name="endValueDelimiter"/> for each encountered <paramref name="startValueDelimiter"/> in the value or a        /// <see cref="FormatException"/> will be thrown. Multiple levels of nesting is supported. If the <paramref name="ignoreDuplicateKeys"/>        /// flag is set to <c>false</c> an <see cref="ArgumentException"/> will be thrown when all key parameters are not unique. Note        /// that keys within nested expressions are considered separate key/value pair strings and are not considered when checking        /// for duplicate keys.        /// </para>        /// </remarks>        /// <exception cref="ArgumentNullException">value is null.</exception>        /// <exception cref="ArgumentException">All delimiters must be unique -or- all keys must be unique when        /// <paramref name="ignoreDuplicateKeys"/> is set to <c>false</c>.</exception>        /// <exception cref="FormatException">Total nested key/value pair expressions are mismatched -or- encountered        /// <paramref name="endValueDelimiter"/> before <paramref name="startValueDelimiter"/>.</exception>        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]        public static Dictionary<string, string> ParseKeyValuePairs(this string value, char parameterDelimiter = ';', char keyValueDelimiter = '=', char startValueDelimiter = '{', char endValueDelimiter = '}', bool ignoreDuplicateKeys = true)        {            if (value == null)                throw new ArgumentNullException(nameof(value));             if (parameterDelimiter == keyValueDelimiter ||                parameterDelimiter == startValueDelimiter ||                parameterDelimiter == endValueDelimiter ||                keyValueDelimiter == startValueDelimiter ||                keyValueDelimiter == endValueDelimiter ||                startValueDelimiter == endValueDelimiter)                throw new ArgumentException("All delimiters must be unique");             Dictionary<string, string> keyValuePairs = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);            StringBuilder escapedValue = new StringBuilder();            string escapedParameterDelimiter = parameterDelimiter.RegexEncode();            string escapedKeyValueDelimiter = keyValueDelimiter.RegexEncode();            string escapedStartValueDelimiter = startValueDelimiter.RegexEncode();            string escapedEndValueDelimiter = endValueDelimiter.RegexEncode();            string backslashDelimiter = '\\'.RegexEncode();            string[] elements;            string key, unescapedValue;            bool valueEscaped = false;            int delimiterDepth = 0;            char character;             // Escape any parameter or key/value delimiters within tagged value sequences            //      For example, the following string:            //          "normalKVP=-1; nestedKVP={p1=true; p2=false}")            //      would be encoded as:            //          "normalKVP=-1; nestedKVP=p1\\u003dtrue\\u003b p2\\u003dfalse")            for (int x = 0; x < value.Length; x++)            {                character = value[x];                 if (character == startValueDelimiter)                {                    if (!valueEscaped)                    {                        valueEscaped = true;                        continue;   // Don't add tag start delimiter to final value                    }                     // Handle nested delimiters                    delimiterDepth++;                }                 if (character == endValueDelimiter)                {                    if (valueEscaped)                    {                        if (delimiterDepth > 0)                        {                            // Handle nested delimiters                            delimiterDepth--;                        }                        else                        {                            valueEscaped = false;                            continue;   // Don't add tag stop delimiter to final value                        }                    }                    else                    {                        throw new FormatException($"Failed to parse key/value pairs: invalid delimiter mismatch. Encountered end value delimiter \'{endValueDelimiter}\' before start value delimiter \'{startValueDelimiter}\'.");                    }                }                 if (valueEscaped)                {                    // Escape any delimiter characters inside nested key/value pair                    if (character == parameterDelimiter)                        escapedValue.Append(escapedParameterDelimiter);                    else if (character == keyValueDelimiter)                        escapedValue.Append(escapedKeyValueDelimiter);                    else if (character == startValueDelimiter)                        escapedValue.Append(escapedStartValueDelimiter);                    else if (character == endValueDelimiter)                        escapedValue.Append(escapedEndValueDelimiter);                    else if (character == '\\')                        escapedValue.Append(backslashDelimiter);                    else                        escapedValue.Append(character);                }                else                {                    if (character == '\\')                        escapedValue.Append(backslashDelimiter);                    else                        escapedValue.Append(character);                }            }             if (delimiterDepth != 0 || valueEscaped)            {                // If value is still escaped, tagged expression was not terminated                if (valueEscaped)                    delimiterDepth = 1;                 throw new FormatException($"Failed to parse key/value pairs: invalid delimiter mismatch. Encountered more {(delimiterDepth > 0 ? "start value delimiters \'" + startValueDelimiter + "\'" : "end value delimiters \'" + endValueDelimiter + "\'")} than {(delimiterDepth < 0 ? "start value delimiters \'" + startValueDelimiter + "\'" : "end value delimiters \'" + endValueDelimiter + "\'")}.");            }             // Parse key/value pairs from escaped value            foreach (string parameter in escapedValue.ToString().Split(parameterDelimiter))            {                // Parse out parameter's key/value elements                elements = parameter.Split(keyValueDelimiter);                 if (elements.Length == 2)                {                    // Get key expression                    key = elements[0].Trim(); 

Lines 1–500 of 2312.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t